Browse Source

Merge pull request #8548 from tagcup/etc2comp

Add ETC1/ETC2 compression support though etc2comp.
Juan Linietsky 8 years ago
parent
commit
a8e845a474
61 changed files with 12981 additions and 2765 deletions
  1. 7 2
      .travis.yml
  2. 11 11
      core/image.cpp
  3. 4 4
      core/image.h
  4. 17 0
      core/math/math_funcs.h
  5. 3 1
      doc/base/classes.xml
  6. 9 5
      editor/import/resource_importer_texture.cpp
  7. 37 0
      modules/etc/SCsub
  8. 0 0
      modules/etc/config.py
  9. 181 0
      modules/etc/image_etc.cpp
  10. 1 1
      modules/etc/image_etc.h
  11. 3 3
      modules/etc/register_types.cpp
  12. 2 2
      modules/etc/register_types.h
  13. 0 0
      modules/etc/texture_loader_pkm.cpp
  14. 0 0
      modules/etc/texture_loader_pkm.h
  15. 0 20
      modules/etc1/SCsub
  16. 0 183
      modules/etc1/image_etc.cpp
  17. 12 11
      thirdparty/README.md
  18. 7 0
      thirdparty/etc2comp/AUTHORS
  19. 128 0
      thirdparty/etc2comp/Etc.cpp
  20. 71 0
      thirdparty/etc2comp/Etc.h
  21. 425 0
      thirdparty/etc2comp/EtcBlock4x4.cpp
  22. 172 0
      thirdparty/etc2comp/EtcBlock4x4.h
  23. 261 0
      thirdparty/etc2comp/EtcBlock4x4Encoding.cpp
  24. 148 0
      thirdparty/etc2comp/EtcBlock4x4Encoding.h
  25. 315 0
      thirdparty/etc2comp/EtcBlock4x4EncodingBits.h
  26. 1281 0
      thirdparty/etc2comp/EtcBlock4x4Encoding_ETC1.cpp
  27. 186 0
      thirdparty/etc2comp/EtcBlock4x4Encoding_ETC1.h
  28. 429 0
      thirdparty/etc2comp/EtcBlock4x4Encoding_R11.cpp
  29. 122 0
      thirdparty/etc2comp/EtcBlock4x4Encoding_R11.h
  30. 447 0
      thirdparty/etc2comp/EtcBlock4x4Encoding_RG11.cpp
  31. 86 0
      thirdparty/etc2comp/EtcBlock4x4Encoding_RG11.h
  32. 1730 0
      thirdparty/etc2comp/EtcBlock4x4Encoding_RGB8.cpp
  33. 96 0
      thirdparty/etc2comp/EtcBlock4x4Encoding_RGB8.h
  34. 1819 0
      thirdparty/etc2comp/EtcBlock4x4Encoding_RGB8A1.cpp
  35. 129 0
      thirdparty/etc2comp/EtcBlock4x4Encoding_RGB8A1.h
  36. 474 0
      thirdparty/etc2comp/EtcBlock4x4Encoding_RGBA8.cpp
  37. 121 0
      thirdparty/etc2comp/EtcBlock4x4Encoding_RGBA8.h
  38. 64 0
      thirdparty/etc2comp/EtcColor.h
  39. 321 0
      thirdparty/etc2comp/EtcColorFloatRGBA.h
  40. 67 0
      thirdparty/etc2comp/EtcConfig.h
  41. 173 0
      thirdparty/etc2comp/EtcDifferentialTrys.cpp
  42. 97 0
      thirdparty/etc2comp/EtcDifferentialTrys.h
  43. 54 0
      thirdparty/etc2comp/EtcErrorMetric.h
  44. 390 0
      thirdparty/etc2comp/EtcFile.cpp
  45. 136 0
      thirdparty/etc2comp/EtcFile.h
  46. 185 0
      thirdparty/etc2comp/EtcFileHeader.cpp
  47. 146 0
      thirdparty/etc2comp/EtcFileHeader.h
  48. 401 0
      thirdparty/etc2comp/EtcFilter.cpp
  49. 244 0
      thirdparty/etc2comp/EtcFilter.h
  50. 685 0
      thirdparty/etc2comp/EtcImage.cpp
  51. 249 0
      thirdparty/etc2comp/EtcImage.h
  52. 85 0
      thirdparty/etc2comp/EtcIndividualTrys.cpp
  53. 95 0
      thirdparty/etc2comp/EtcIndividualTrys.h
  54. 64 0
      thirdparty/etc2comp/EtcMath.cpp
  55. 40 0
      thirdparty/etc2comp/EtcMath.h
  56. 228 0
      thirdparty/etc2comp/EtcSortedBlockList.cpp
  57. 124 0
      thirdparty/etc2comp/EtcSortedBlockList.h
  58. 202 0
      thirdparty/etc2comp/LICENSE
  59. 197 0
      thirdparty/etc2comp/README.md
  60. 0 2446
      thirdparty/rg-etc1/rg_etc1.cpp
  61. 0 76
      thirdparty/rg-etc1/rg_etc1.h

+ 7 - 2
.travis.yml

@@ -1,5 +1,7 @@
 language: cpp
 
+dist: trusty
+
 sudo: false
 
 compiler:
@@ -73,7 +75,6 @@ addons:
       # For style checks.
       - clang-format-3.9
 
-
 before_script:
   - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew update; brew install scons; fi
   - if [ "$TRAVIS_OS_NAME" = "osx" ] && [ "$GODOT_TARGET" = "android" ]; then
@@ -86,5 +87,9 @@ script:
   - if [ "$STATIC_CHECKS" = "yes" ]; then
       sh ./misc/travis/clang-format.sh;
     else
-      scons platform=$GODOT_TARGET CXX=$CXX openssl=builtin;
+      if [ "$TRAVIS_OS_NAME" = "windows" ]; then
+        scons platform=$GODOT_TARGET CXX=$CXX openssl=builtin;
+      else
+        scons platform=$GODOT_TARGET bits=64 CXX=$CXX openssl=builtin;
+      fi
     fi

+ 11 - 11
core/image.cpp

@@ -1483,16 +1483,16 @@ Error Image::decompress() {
 		_image_decompress_bc(this);
 	else if (format >= FORMAT_PVRTC2 && format <= FORMAT_PVRTC4A && _image_decompress_pvrtc)
 		_image_decompress_pvrtc(this);
-	else if (format == FORMAT_ETC && _image_decompress_etc)
-		_image_decompress_etc(this);
-	else if (format >= FORMAT_ETC2_R11 && format <= FORMAT_ETC2_RGB8A1 && _image_decompress_etc)
+	else if (format == FORMAT_ETC && _image_decompress_etc1)
+		_image_decompress_etc1(this);
+	else if (format >= FORMAT_ETC2_R11 && format <= FORMAT_ETC2_RGB8A1 && _image_decompress_etc1)
 		_image_decompress_etc2(this);
 	else
 		return ERR_UNAVAILABLE;
 	return OK;
 }
 
-Error Image::compress(CompressMode p_mode, bool p_for_srgb) {
+Error Image::compress(CompressMode p_mode, bool p_for_srgb, float p_lossy_quality) {
 
 	switch (p_mode) {
 
@@ -1513,13 +1513,13 @@ Error Image::compress(CompressMode p_mode, bool p_for_srgb) {
 		} break;
 		case COMPRESS_ETC: {
 
-			ERR_FAIL_COND_V(!_image_compress_etc_func, ERR_UNAVAILABLE);
-			_image_compress_etc_func(this);
+			ERR_FAIL_COND_V(!_image_compress_etc1_func, ERR_UNAVAILABLE);
+			_image_compress_etc1_func(this, p_lossy_quality);
 		} break;
 		case COMPRESS_ETC2: {
 
-			ERR_FAIL_COND_V(!_image_compress_etc_func, ERR_UNAVAILABLE);
-			_image_compress_etc_func(this);
+			ERR_FAIL_COND_V(!_image_compress_etc2_func, ERR_UNAVAILABLE);
+			_image_compress_etc2_func(this, p_lossy_quality);
 		} break;
 	}
 
@@ -1652,11 +1652,11 @@ Ref<Image> (*Image::_jpg_mem_loader_func)(const uint8_t *, int) = NULL;
 void (*Image::_image_compress_bc_func)(Image *, bool) = NULL;
 void (*Image::_image_compress_pvrtc2_func)(Image *) = NULL;
 void (*Image::_image_compress_pvrtc4_func)(Image *) = NULL;
-void (*Image::_image_compress_etc_func)(Image *) = NULL;
-void (*Image::_image_compress_etc2_func)(Image *) = NULL;
+void (*Image::_image_compress_etc1_func)(Image *, float) = NULL;
+void (*Image::_image_compress_etc2_func)(Image *, float) = NULL;
 void (*Image::_image_decompress_pvrtc)(Image *) = NULL;
 void (*Image::_image_decompress_bc)(Image *) = NULL;
-void (*Image::_image_decompress_etc)(Image *) = NULL;
+void (*Image::_image_decompress_etc1)(Image *) = NULL;
 void (*Image::_image_decompress_etc2)(Image *) = NULL;
 
 PoolVector<uint8_t> (*Image::lossy_packer)(const Ref<Image> &, float) = NULL;

+ 4 - 4
core/image.h

@@ -117,12 +117,12 @@ public:
 	static void (*_image_compress_bc_func)(Image *, bool p_srgb);
 	static void (*_image_compress_pvrtc2_func)(Image *);
 	static void (*_image_compress_pvrtc4_func)(Image *);
-	static void (*_image_compress_etc_func)(Image *);
-	static void (*_image_compress_etc2_func)(Image *);
+	static void (*_image_compress_etc1_func)(Image *, float);
+	static void (*_image_compress_etc2_func)(Image *, float);
 
 	static void (*_image_decompress_pvrtc)(Image *);
 	static void (*_image_decompress_bc)(Image *);
-	static void (*_image_decompress_etc)(Image *);
+	static void (*_image_decompress_etc1)(Image *);
 	static void (*_image_decompress_etc2)(Image *);
 
 	static PoolVector<uint8_t> (*lossy_packer)(const Ref<Image> &p_image, float p_quality);
@@ -267,7 +267,7 @@ public:
 		COMPRESS_ETC2,
 	};
 
-	Error compress(CompressMode p_mode = COMPRESS_S3TC, bool p_for_srgb = false);
+	Error compress(CompressMode p_mode = COMPRESS_S3TC, bool p_for_srgb = false, float p_lossy_quality = 0.7);
 	Error decompress();
 	bool is_compressed() const;
 

+ 17 - 0
core/math/math_funcs.h

@@ -110,6 +110,15 @@ public:
 	static _ALWAYS_INLINE_ bool is_inf(double p_val) {
 #ifdef _MSC_VER
 		return !_finite(p_val);
+// workaround for mingw builds on travis
+#elif defined(__MINGW32__) || defined(__MINGW64__)
+		union {
+			uint64_t u;
+			double f;
+		} ieee754;
+		ieee754.f = p_val;
+		return ((unsigned)(ieee754.u >> 32) & 0x7fffffff) == 0x7ff00000 &&
+			   ((unsigned)ieee754.u == 0);
 #else
 		return isinf(p_val);
 #endif
@@ -118,6 +127,14 @@ public:
 	static _ALWAYS_INLINE_ bool is_inf(float p_val) {
 #ifdef _MSC_VER
 		return !_finite(p_val);
+// workaround for mingw builds on travis
+#elif defined(__MINGW32__) || defined(__MINGW64__)
+		union {
+			uint32_t u;
+			float f;
+		} ieee754;
+		ieee754.f = p_val;
+		return (ieee754.u & 0x7fffffff) == 0x7f800000;
 #else
 		return isinf(p_val);
 #endif

+ 3 - 1
doc/base/classes.xml

@@ -18490,8 +18490,10 @@
 			</return>
 			<argument index="0" name="format" type="int" default="0">
 			</argument>
+			<argument index="0" name="quality" type="float" default="0">
+			</argument>
 			<description>
-				Return a new compressed [Image] from this [Image] using one of [Image].COMPRESS_*.
+				Return a new compressed [Image] from this [Image] using one of [Image].COMPRESS_*. Quality only affects ETC1 and ETC2 encoding.
 			</description>
 		</method>
 		<method name="converted">

+ 9 - 5
editor/import/resource_importer_texture.cpp

@@ -144,8 +144,12 @@ String ResourceImporterTexture::get_resource_type() const {
 
 bool ResourceImporterTexture::get_option_visibility(const String &p_option, const Map<StringName, Variant> &p_options) const {
 
-	if (p_option == "compress/lossy_quality" && int(p_options["compress/mode"]) != COMPRESS_LOSSY)
-		return false;
+	if (p_option == "compress/lossy_quality") {
+		int compress_mode = int(p_options["compress/mode"]);
+		if (compress_mode != COMPRESS_LOSSY && compress_mode != COMPRESS_VIDEO_RAM) {
+			return false;
+		}
+	}
 
 	return true;
 }
@@ -277,7 +281,7 @@ void ResourceImporterTexture::_save_stex(const Ref<Image> &p_image, const String
 			if (p_force_rgbe && image->get_format() >= Image::FORMAT_R8 && image->get_format() <= Image::FORMAT_RGBE9995) {
 				image->convert(Image::FORMAT_RGBE9995);
 			} else {
-				image->compress(p_vram_compression, p_texture_flags & VS::TEXTURE_FLAG_CONVERT_TO_LINEAR);
+				image->compress(p_vram_compression, p_texture_flags & VS::TEXTURE_FLAG_CONVERT_TO_LINEAR, p_lossy_quality);
 			}
 
 			format |= image->get_format();
@@ -382,8 +386,8 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String
 		//Android, GLES 2.x
 		_save_stex(image, p_save_path + ".etc.stex", compress_mode, lossy, Image::COMPRESS_ETC, mipmaps, tex_flags, stream, detect_3d, detect_srgb, force_rgbe);
 		r_platform_variants->push_back("etc");
-		//_save_stex(image,p_save_path+".etc2.stex",compress_mode,lossy,Image::COMPRESS_ETC2,mipmaps,tex_flags,stream);
-		//r_platform_variants->push_back("etc2");
+		_save_stex(image, p_save_path + ".etc2.stex", compress_mode, lossy, Image::COMPRESS_ETC2, mipmaps, tex_flags, stream, detect_3d, detect_srgb, force_rgbe);
+		r_platform_variants->push_back("etc2");
 		_save_stex(image, p_save_path + ".s3tc.stex", compress_mode, lossy, Image::COMPRESS_S3TC, mipmaps, tex_flags, stream, detect_3d, detect_srgb, force_rgbe);
 		r_platform_variants->push_back("s3tc");
 

+ 37 - 0
modules/etc/SCsub

@@ -0,0 +1,37 @@
+#!/usr/bin/env python
+
+Import('env')
+Import('env_modules')
+
+env_etc = env_modules.Clone()
+
+# Thirdparty source files
+# Not unbundled so far since not widespread as shared library
+thirdparty_dir = "#thirdparty/etc2comp/"
+thirdparty_sources = [
+	"EtcBlock4x4.cpp",
+	"EtcBlock4x4Encoding.cpp",
+	"EtcBlock4x4Encoding_ETC1.cpp",
+	"EtcBlock4x4Encoding_R11.cpp",
+	"EtcBlock4x4Encoding_RG11.cpp",
+	"EtcBlock4x4Encoding_RGB8A1.cpp",
+	"EtcBlock4x4Encoding_RGB8.cpp",
+	"EtcBlock4x4Encoding_RGBA8.cpp",
+	"Etc.cpp",
+	"EtcDifferentialTrys.cpp",
+	"EtcFilter.cpp",
+	"EtcImage.cpp",
+	"EtcIndividualTrys.cpp",
+	"EtcMath.cpp",
+	"EtcSortedBlockList.cpp",
+]
+thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+env_etc.add_source_files(env.modules_sources, thirdparty_sources)
+env_etc.Append(CPPPATH=[thirdparty_dir])
+
+# Godot source files
+env_etc.add_source_files(env.modules_sources, "*.cpp")
+
+# upstream uses c++11
+env_etc.Append(CXXFLAGS="-std=gnu++11")

+ 0 - 0
modules/etc1/config.py → modules/etc/config.py


+ 181 - 0
modules/etc/image_etc.cpp

@@ -0,0 +1,181 @@
+/*************************************************************************/
+/*  image_etc.cpp                                                        */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                    http://www.godotengine.org                         */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+#include "image_etc.h"
+#include "Etc.h"
+#include "EtcFilter.h"
+#include "image.h"
+#include "os/copymem.h"
+#include "os/os.h"
+#include "print_string.h"
+
+static Image::Format _get_etc2_mode(Image::DetectChannels format) {
+	switch (format) {
+		case Image::DETECTED_L:
+		case Image::DETECTED_R:
+			return Image::FORMAT_ETC2_R11;
+
+		case Image::DETECTED_RG:
+			return Image::FORMAT_ETC2_RG11;
+
+		case Image::DETECTED_RGB:
+			return Image::FORMAT_ETC2_RGB8;
+
+		case Image::DETECTED_RGBA:
+			return Image::FORMAT_ETC2_RGBA8;
+
+			// TODO: would be nice if we could use FORMAT_ETC2_RGB8A1 for FORMAT_RGBA5551
+	}
+
+	ERR_FAIL_COND_V(true, Image::FORMAT_MAX);
+}
+
+static Etc::Image::Format _image_format_to_etc2comp_format(Image::Format format) {
+	switch (format) {
+		case Image::FORMAT_ETC:
+			return Etc::Image::Format::ETC1;
+
+		case Image::FORMAT_ETC2_R11:
+			return Etc::Image::Format::R11;
+
+		case Image::FORMAT_ETC2_R11S:
+			return Etc::Image::Format::SIGNED_R11;
+
+		case Image::FORMAT_ETC2_RG11:
+			return Etc::Image::Format::RG11;
+
+		case Image::FORMAT_ETC2_RG11S:
+			return Etc::Image::Format::SIGNED_RG11;
+
+		case Image::FORMAT_ETC2_RGB8:
+			return Etc::Image::Format::RGB8;
+
+		case Image::FORMAT_ETC2_RGBA8:
+			return Etc::Image::Format::RGBA8;
+
+		case Image::FORMAT_ETC2_RGB8A1:
+			return Etc::Image::Format::RGB8A1;
+	}
+
+	ERR_FAIL_COND_V(true, Etc::Image::Format::UNKNOWN);
+}
+
+static void _decompress_etc1(Image *p_img) {
+	// not implemented, to be removed
+}
+
+static void _decompress_etc2(Image *p_img) {
+	// not implemented, to be removed
+}
+
+static void _compress_etc(Image *p_img, float p_lossy_quality, bool force_etc1_format) {
+	Image::Format img_format = p_img->get_format();
+	Image::DetectChannels detected_channels = p_img->get_detected_channels();
+
+	if (img_format >= Image::FORMAT_DXT1) {
+		return; //do not compress, already compressed
+	}
+
+	if (img_format > Image::FORMAT_RGBA8) {
+		// TODO: we should be able to handle FORMAT_RGBA4444 and FORMAT_RGBA5551 eventually
+		return;
+	}
+
+	int imgw = p_img->get_width(), imgh = p_img->get_height();
+	ERR_FAIL_COND(nearest_power_of_2(imgw) != imgw || nearest_power_of_2(imgh) != imgh);
+
+	Image::Format etc_format = force_etc1_format ? Image::FORMAT_ETC : _get_etc2_mode(detected_channels);
+
+	Ref<Image> img = p_img->duplicate();
+
+	if (img->get_format() != Image::FORMAT_RGBA8)
+		img->convert(Image::FORMAT_RGBA8); //still uses RGBA to convert
+
+	PoolVector<uint8_t>::Read r = img->get_data().read();
+
+	int target_size = Image::get_image_data_size(imgw, imgh, etc_format, p_img->has_mipmaps() ? -1 : 0);
+	int mmc = p_img->has_mipmaps() ? Image::get_image_required_mipmaps(imgw, imgh, etc_format) : 0;
+
+	PoolVector<uint8_t> dst_data;
+	dst_data.resize(target_size);
+
+	PoolVector<uint8_t>::Write w = dst_data.write();
+
+	// prepare parameters to be passed to etc2comp
+	int num_cpus = OS::get_singleton()->get_processor_count();
+	int encoding_time = 0;
+	float effort = CLAMP(p_lossy_quality * 100, 0, 100);
+	Etc::ErrorMetric error_metric = Etc::ErrorMetric::BT709; // NOTE: we can experiment with other error metrics
+	Etc::Image::Format etc2comp_etc_format = _image_format_to_etc2comp_format(etc_format);
+
+	int wofs = 0;
+	for (int i = 0; i < mmc + 1; i++) {
+		// convert source image to internal etc2comp format (which is equivalent to Image::FORMAT_RGBAF)
+		// NOTE: We can alternatively add a case to Image::convert to handle Image::FORMAT_RGBAF conversion.
+		int mipmap_ofs = 0, mipmap_size = 0, mipmap_w = 0, mipmap_h = 0;
+		img->get_mipmap_offset_size_and_dimensions(i, mipmap_ofs, mipmap_size, mipmap_w, mipmap_h);
+		const uint8_t *src = &r[mipmap_ofs];
+
+		Etc::ColorFloatRGBA *src_rgba_f = new Etc::ColorFloatRGBA[mipmap_w * mipmap_h];
+		for (int i = 0; i < mipmap_w * mipmap_h; i++) {
+			int si = i * 4; // RGBA8
+			src_rgba_f[i] = Etc::ColorFloatRGBA::ConvertFromRGBA8(src[si], src[si + 1], src[si + 2], src[si + 3]);
+		}
+
+		unsigned char *etc_data = NULL;
+		unsigned int etc_data_len = 0;
+		unsigned int extended_width = 0, extended_height = 0;
+		Etc::Encode((float *)src_rgba_f, mipmap_w, mipmap_h, etc2comp_etc_format, error_metric, effort, num_cpus, num_cpus, &etc_data, &etc_data_len, &extended_width, &extended_height, &encoding_time);
+
+		memcpy(&w[wofs], etc_data, etc_data_len);
+		wofs += etc_data_len;
+
+		delete[] etc_data;
+		delete[] src_rgba_f;
+	}
+
+	p_img->create(imgw, imgh, mmc > 1 ? true : false, etc_format, dst_data);
+}
+
+static void _compress_etc1(Image *p_img, float p_lossy_quality) {
+	_compress_etc(p_img, p_lossy_quality, true);
+}
+
+static void _compress_etc2(Image *p_img, float p_lossy_quality) {
+	_compress_etc(p_img, p_lossy_quality, false);
+}
+
+void _register_etc_compress_func() {
+
+	Image::_image_compress_etc1_func = _compress_etc1;
+	//Image::_image_decompress_etc1 = _decompress_etc1;
+
+	Image::_image_compress_etc2_func = _compress_etc2;
+	//Image::_image_decompress_etc2 = _decompress_etc2;
+}

+ 1 - 1
modules/etc1/image_etc.h → modules/etc/image_etc.h

@@ -30,6 +30,6 @@
 #ifndef IMAGE_ETC1_H
 #define IMAGE_ETC1_H
 
-void _register_etc1_compress_func();
+void _register_etc_compress_func();
 
 #endif // IMAGE_ETC_H

+ 3 - 3
modules/etc1/register_types.cpp → modules/etc/register_types.cpp

@@ -34,15 +34,15 @@
 
 static ResourceFormatPKM *resource_loader_pkm = NULL;
 
-void register_etc1_types() {
+void register_etc_types() {
 
 	resource_loader_pkm = memnew(ResourceFormatPKM);
 	ResourceLoader::add_resource_format_loader(resource_loader_pkm);
 
-	_register_etc1_compress_func();
+	_register_etc_compress_func();
 }
 
-void unregister_etc1_types() {
+void unregister_etc_types() {
 
 	memdelete(resource_loader_pkm);
 }

+ 2 - 2
modules/etc1/register_types.h → modules/etc/register_types.h

@@ -27,5 +27,5 @@
 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /*************************************************************************/
-void register_etc1_types();
-void unregister_etc1_types();
+void register_etc_types();
+void unregister_etc_types();

+ 0 - 0
modules/etc1/texture_loader_pkm.cpp → modules/etc/texture_loader_pkm.cpp


+ 0 - 0
modules/etc1/texture_loader_pkm.h → modules/etc/texture_loader_pkm.h


+ 0 - 20
modules/etc1/SCsub

@@ -1,20 +0,0 @@
-#!/usr/bin/env python
-
-Import('env')
-Import('env_modules')
-
-env_etc1 = env_modules.Clone()
-
-# Thirdparty source files
-# Not unbundled so far since not widespread as shared library
-thirdparty_dir = "#thirdparty/rg-etc1/"
-thirdparty_sources = [
-    "rg_etc1.cpp",
-]
-thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
-
-env_etc1.add_source_files(env.modules_sources, thirdparty_sources)
-env_etc1.Append(CPPPATH=[thirdparty_dir])
-
-# Godot source files
-env_etc1.add_source_files(env.modules_sources, "*.cpp")

+ 0 - 183
modules/etc1/image_etc.cpp

@@ -1,183 +0,0 @@
-/*************************************************************************/
-/*  image_etc.cpp                                                        */
-/*************************************************************************/
-/*                       This file is part of:                           */
-/*                           GODOT ENGINE                                */
-/*                    http://www.godotengine.org                         */
-/*************************************************************************/
-/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur.                 */
-/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md)    */
-/*                                                                       */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the       */
-/* "Software"), to deal in the Software without restriction, including   */
-/* without limitation the rights to use, copy, modify, merge, publish,   */
-/* distribute, sublicense, and/or sell copies of the Software, and to    */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions:                                             */
-/*                                                                       */
-/* The above copyright notice and this permission notice shall be        */
-/* included in all copies or substantial portions of the Software.       */
-/*                                                                       */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
-/*************************************************************************/
-#include "image_etc.h"
-#include "image.h"
-#include "os/copymem.h"
-#include "print_string.h"
-#include "rg_etc1.h"
-static void _decompress_etc(Image *p_img) {
-
-	ERR_FAIL_COND(p_img->get_format() != Image::FORMAT_ETC);
-
-	int imgw = p_img->get_width();
-	int imgh = p_img->get_height();
-	PoolVector<uint8_t> src = p_img->get_data();
-	PoolVector<uint8_t> dst;
-
-	PoolVector<uint8_t>::Read r = src.read();
-
-	int mmc = p_img->get_mipmap_count();
-
-	for (int i = 0; i <= mmc; i++) {
-
-		dst.resize(dst.size() + imgw * imgh * 3);
-		const uint8_t *srcbr = &r[p_img->get_mipmap_offset(i)];
-		PoolVector<uint8_t>::Write w = dst.write();
-
-		uint8_t *wptr = &w[dst.size() - imgw * imgh * 3];
-
-		int bw = MAX(imgw / 4, 1);
-		int bh = MAX(imgh / 4, 1);
-
-		for (int y = 0; y < bh; y++) {
-
-			for (int x = 0; x < bw; x++) {
-
-				uint8_t block[4 * 4 * 4];
-
-				rg_etc1::unpack_etc1_block(srcbr, (unsigned int *)block);
-				srcbr += 8;
-
-				int maxx = MIN(imgw, 4);
-				int maxy = MIN(imgh, 4);
-
-				for (int yy = 0; yy < maxy; yy++) {
-
-					for (int xx = 0; xx < maxx; xx++) {
-
-						uint32_t src_ofs = (yy * 4 + xx) * 4;
-						uint32_t dst_ofs = ((y * 4 + yy) * imgw + x * 4 + xx) * 3;
-						wptr[dst_ofs + 0] = block[src_ofs + 0];
-						wptr[dst_ofs + 1] = block[src_ofs + 1];
-						wptr[dst_ofs + 2] = block[src_ofs + 2];
-					}
-				}
-			}
-		}
-
-		imgw = MAX(1, imgw / 2);
-		imgh = MAX(1, imgh / 2);
-	}
-
-	r = PoolVector<uint8_t>::Read();
-	//print_line("Re Creating ETC into regular image: w "+itos(p_img->get_width())+" h "+itos(p_img->get_height())+" mm "+itos(p_img->get_mipmaps()));
-	bool needs_mipmaps = p_img->has_mipmaps();
-	p_img->create(p_img->get_width(), p_img->get_height(), p_img->has_mipmaps(), Image::FORMAT_RGB8, dst);
-	if (needs_mipmaps)
-		p_img->generate_mipmaps();
-}
-
-static void _compress_etc(Image *p_img) {
-
-	Ref<Image> img = p_img->duplicate();
-
-	int imgw = img->get_width(), imgh = img->get_height();
-
-	ERR_FAIL_COND(nearest_power_of_2(imgw) != imgw || nearest_power_of_2(imgh) != imgh);
-
-	if (img->get_format() != Image::FORMAT_RGB8)
-		img->convert(Image::FORMAT_RGB8);
-
-	PoolVector<uint8_t> res_data;
-	PoolVector<uint8_t> dst_data;
-	PoolVector<uint8_t>::Read r = img->get_data().read();
-
-	int target_size = Image::get_image_data_size(p_img->get_width(), p_img->get_height(), Image::FORMAT_ETC, p_img->has_mipmaps() ? -1 : 0);
-	int mmc = p_img->has_mipmaps() ? Image::get_image_required_mipmaps(p_img->get_width(), p_img->get_height(), Image::FORMAT_ETC) : 0;
-
-	dst_data.resize(target_size);
-	int mc = 0;
-	int ofs = 0;
-	PoolVector<uint8_t>::Write w = dst_data.write();
-
-	rg_etc1::etc1_pack_params pp;
-	pp.m_quality = rg_etc1::cLowQuality;
-	for (int i = 0; i <= mmc; i++) {
-
-		int bw = MAX(imgw / 4, 1);
-		int bh = MAX(imgh / 4, 1);
-		const uint8_t *src = &r[img->get_mipmap_offset(i)];
-		int mmsize = MAX(bw, 1) * MAX(bh, 1) * 8;
-
-		uint8_t *dst = &w[ofs];
-		ofs += mmsize;
-
-		//print_line("bh: "+itos(bh)+" bw: "+itos(bw));
-
-		for (int y = 0; y < bh; y++) {
-
-			for (int x = 0; x < bw; x++) {
-
-				//print_line("x: "+itos(x)+" y: "+itos(y));
-
-				uint8_t block[4 * 4 * 4];
-				zeromem(block, 4 * 4 * 4);
-				uint8_t cblock[8];
-
-				int maxy = MIN(imgh, 4);
-				int maxx = MIN(imgw, 4);
-
-				for (int yy = 0; yy < maxy; yy++) {
-
-					for (int xx = 0; xx < maxx; xx++) {
-
-						uint32_t dst_ofs = (yy * 4 + xx) * 4;
-						uint32_t src_ofs = ((y * 4 + yy) * imgw + x * 4 + xx) * 3;
-						block[dst_ofs + 0] = src[src_ofs + 0];
-						block[dst_ofs + 1] = src[src_ofs + 1];
-						block[dst_ofs + 2] = src[src_ofs + 2];
-						block[dst_ofs + 3] = 255;
-					}
-				}
-
-				rg_etc1::pack_etc1_block(cblock, (const unsigned int *)block, pp);
-				for (int j = 0; j < 8; j++) {
-
-					dst[j] = cblock[j];
-				}
-
-				dst += 8;
-			}
-		}
-
-		imgw = MAX(1, imgw / 2);
-		imgh = MAX(1, imgh / 2);
-		mc++;
-	}
-
-	p_img->create(p_img->get_width(), p_img->get_height(), (mc - 1) ? true : false, Image::FORMAT_ETC, dst_data);
-}
-
-void _register_etc1_compress_func() {
-
-	rg_etc1::pack_etc1_block_init();
-	Image::_image_compress_etc_func = _compress_etc;
-	Image::_image_decompress_etc = _decompress_etc;
-}

+ 12 - 11
thirdparty/README.md

@@ -37,6 +37,18 @@ Check the diff of enet.h, protocol.c, and host.c with the 1.3.13
 tarball before the next update.
 
 
+## etc2comp
+
+- Upstream: https://github.com/google/etc2comp
+- Version: 9cd0f9c (git)
+- License: Apache
+
+Files extracted from upstream source:
+
+- all .cpp and .h files in EtcLib/
+- README.md, LICENSE, AUTHORS
+
+
 ## fonts
 
 - Upstream: ?
@@ -296,17 +308,6 @@ Files extracted from upstream source:
 - LICENSE.TXT
 
 
-## rg-etc1
-
-- Upstream: https://github.com/richgel999/rg-etc1
-- Version: 1.04
-- License: zlib
-
-Files extracted from upstream source:
-
-- `rg_etc1.{cpp,h}`
-
-
 ## rtaudio
 
 - Upstream: http://www.music.mcgill.ca/~gary/rtaudio/

+ 7 - 0
thirdparty/etc2comp/AUTHORS

@@ -0,0 +1,7 @@
+# This is the list of Etc2Comp authors for copyright purposes.
+#
+# This does not necessarily list everyone who has contributed code, since in
+# some cases, their employer may be the copyright holder.  To see the full list
+# of contributors, see the revision history in source control.
+Google Inc.
+Blue Shift Inc.

+ 128 - 0
thirdparty/etc2comp/Etc.cpp

@@ -0,0 +1,128 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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 "EtcConfig.h"
+#include "Etc.h"
+#include "EtcFilter.h"
+
+#include <string.h>
+
+namespace Etc
+{
+	// ----------------------------------------------------------------------------------------------------
+	// C-style inteface to the encoder
+	//
+	void Encode(float *a_pafSourceRGBA,
+				unsigned int a_uiSourceWidth, 
+				unsigned int a_uiSourceHeight,
+				Image::Format a_format,
+				ErrorMetric a_eErrMetric,
+				float a_fEffort,
+				unsigned int a_uiJobs,
+				unsigned int a_uiMaxJobs,
+				unsigned char **a_ppaucEncodingBits,
+				unsigned int *a_puiEncodingBitsBytes,
+				unsigned int *a_puiExtendedWidth,
+				unsigned int *a_puiExtendedHeight, 
+				int *a_piEncodingTime_ms, bool a_bVerboseOutput)
+	{
+
+		Image image(a_pafSourceRGBA, a_uiSourceWidth,
+					a_uiSourceHeight,
+					a_eErrMetric);
+		image.m_bVerboseOutput = a_bVerboseOutput;
+		image.Encode(a_format, a_eErrMetric, a_fEffort, a_uiJobs, a_uiMaxJobs);
+
+		*a_ppaucEncodingBits = image.GetEncodingBits();
+		*a_puiEncodingBitsBytes = image.GetEncodingBitsBytes();
+		*a_puiExtendedWidth = image.GetExtendedWidth();
+		*a_puiExtendedHeight = image.GetExtendedHeight();
+		*a_piEncodingTime_ms = image.GetEncodingTimeMs();
+	}
+
+	void EncodeMipmaps(float *a_pafSourceRGBA,
+		unsigned int a_uiSourceWidth,
+		unsigned int a_uiSourceHeight,
+		Image::Format a_format,
+		ErrorMetric a_eErrMetric,
+		float a_fEffort,
+		unsigned int a_uiJobs,
+		unsigned int a_uiMaxJobs,
+		unsigned int a_uiMaxMipmaps,
+		unsigned int a_uiMipFilterFlags,
+		RawImage* a_pMipmapImages,
+		int *a_piEncodingTime_ms, 
+		bool a_bVerboseOutput)
+	{
+		auto mipWidth = a_uiSourceWidth;
+		auto mipHeight = a_uiSourceHeight;
+		int totalEncodingTime = 0;
+		for(unsigned int mip = 0; mip < a_uiMaxMipmaps && mipWidth >= 1 && mipHeight >= 1; mip++)
+		{
+			float* pImageData = nullptr;
+			float* pMipImage = nullptr;
+
+			if(mip == 0)
+			{
+				pImageData = a_pafSourceRGBA;
+			}
+			else
+			{
+				pMipImage = new float[mipWidth*mipHeight*4];
+				if(FilterTwoPass(a_pafSourceRGBA, a_uiSourceWidth, a_uiSourceHeight, pMipImage, mipWidth, mipHeight, a_uiMipFilterFlags, Etc::FilterLanczos3) )
+				{
+					pImageData = pMipImage;
+				}
+			}
+
+			if ( pImageData )
+			{
+			
+				Image image(pImageData, mipWidth, mipHeight,	a_eErrMetric);
+
+			image.m_bVerboseOutput = a_bVerboseOutput;
+			image.Encode(a_format, a_eErrMetric, a_fEffort, a_uiJobs, a_uiMaxJobs);
+
+			a_pMipmapImages[mip].paucEncodingBits = std::shared_ptr<unsigned char>(image.GetEncodingBits(), [](unsigned char *p) { delete[] p; });
+			a_pMipmapImages[mip].uiEncodingBitsBytes = image.GetEncodingBitsBytes();
+			a_pMipmapImages[mip].uiExtendedWidth = image.GetExtendedWidth();
+			a_pMipmapImages[mip].uiExtendedHeight = image.GetExtendedHeight();
+
+			totalEncodingTime += image.GetEncodingTimeMs();
+			}
+
+			if(pMipImage)
+			{
+				delete[] pMipImage;
+			}
+
+			if (!pImageData)
+			{
+				break;
+			}
+
+			mipWidth >>= 1;
+			mipHeight >>= 1;
+		}
+
+		*a_piEncodingTime_ms = totalEncodingTime;
+	}
+
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+
+}

+ 71 - 0
thirdparty/etc2comp/Etc.h

@@ -0,0 +1,71 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "EtcConfig.h"
+#include "EtcImage.h"
+#include "EtcColor.h"
+#include "EtcErrorMetric.h"
+#include <memory>
+
+#define ETCCOMP_MIN_EFFORT_LEVEL (0.0f)
+#define ETCCOMP_DEFAULT_EFFORT_LEVEL (40.0f)
+#define ETCCOMP_MAX_EFFORT_LEVEL (100.0f)
+
+namespace Etc
+{
+	class Block4x4EncodingBits;
+
+	struct RawImage
+	{
+		int uiExtendedWidth;
+		int uiExtendedHeight;
+		unsigned int uiEncodingBitsBytes;
+		std::shared_ptr<unsigned char> paucEncodingBits;
+	};
+
+
+
+	// C-style inteface to the encoder
+	void Encode(float *a_pafSourceRGBA,
+				unsigned int a_uiSourceWidth,
+				unsigned int a_uiSourceHeight,
+				Image::Format a_format,
+				ErrorMetric a_eErrMetric,
+				float a_fEffort,
+				unsigned int a_uiJobs,
+				unsigned int a_uimaxJobs,
+				unsigned char **a_ppaucEncodingBits,
+				unsigned int *a_puiEncodingBitsBytes,
+				unsigned int *a_puiExtendedWidth,
+				unsigned int *a_puiExtendedHeight,
+				int *a_piEncodingTime_ms, bool a_bVerboseOutput = false);
+
+	void EncodeMipmaps(float *a_pafSourceRGBA,
+		unsigned int a_uiSourceWidth,
+		unsigned int a_uiSourceHeight,
+		Image::Format a_format,
+		ErrorMetric a_eErrMetric,
+		float a_fEffort,
+		unsigned int a_uiJobs,
+		unsigned int a_uiMaxJobs,
+		unsigned int a_uiMaxMipmaps,
+		unsigned int a_uiMipFilterFlags,
+		RawImage* a_pMipmaps,
+		int *a_piEncodingTime_ms, bool a_bVerboseOutput = false);
+
+}

+ 425 - 0
thirdparty/etc2comp/EtcBlock4x4.cpp

@@ -0,0 +1,425 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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.
+ */
+
+/* 
+EtcBlock4x4.cpp
+
+Implements the state associated with each 4x4 block of pixels in an image
+
+Source images that are not a multiple of 4x4 are extended to fill the Block4x4 using pixels with an 
+alpha of NAN
+
+*/
+
+#include "EtcConfig.h"
+#include "EtcBlock4x4.h"
+
+#include "EtcBlock4x4EncodingBits.h"
+#include "EtcColor.h"
+#include "EtcImage.h"
+#include "EtcColorFloatRGBA.h"
+#include "EtcBlock4x4Encoding_RGB8.h"
+#include "EtcBlock4x4Encoding_RGBA8.h"
+#include "EtcBlock4x4Encoding_RGB8A1.h"
+#include "EtcBlock4x4Encoding_R11.h"
+#include "EtcBlock4x4Encoding_RG11.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+namespace Etc
+{
+	// ETC pixels are scanned vertically.  
+	// this mapping is for when someone wants to scan the ETC pixels horizontally
+	const unsigned int Block4x4::s_auiPixelOrderHScan[PIXELS] = { 0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15 };
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+	Block4x4::Block4x4(void)
+	{
+		m_pimageSource = nullptr;
+		m_uiSourceH = 0;
+		m_uiSourceV = 0;
+
+		m_sourcealphamix = SourceAlphaMix::UNKNOWN;
+		m_boolBorderPixels = false;
+		m_boolPunchThroughPixels = false;
+
+		m_pencoding = nullptr;
+
+		m_errormetric = ErrorMetric::NUMERIC;
+
+	}
+	Block4x4::~Block4x4()
+	{
+		m_pimageSource = nullptr;
+		if (m_pencoding)
+		{
+			delete m_pencoding;
+			m_pencoding = nullptr;
+		}
+	}
+	// ----------------------------------------------------------------------------------------------------
+	// initialization prior to encoding from a source image
+	// [a_uiSourceH,a_uiSourceV] is the location of the block in a_pimageSource
+	// a_paucEncodingBits is the place to store the final encoding
+	// a_errormetric is used for finding the best encoding
+	//
+	void Block4x4::InitFromSource(Image *a_pimageSource, 
+									unsigned int a_uiSourceH, unsigned int a_uiSourceV,
+									unsigned char *a_paucEncodingBits,
+									ErrorMetric a_errormetric)
+	{
+
+		Block4x4();
+
+		m_pimageSource = a_pimageSource;
+		m_uiSourceH = a_uiSourceH;
+		m_uiSourceV = a_uiSourceV;
+		m_errormetric = a_errormetric;
+
+		SetSourcePixels();
+
+		// set block encoder function
+		switch (m_pimageSource->GetFormat())
+		{
+		case Image::Format::ETC1:
+			m_pencoding = new Block4x4Encoding_ETC1;
+			break;
+
+		case Image::Format::RGB8:
+		case Image::Format::SRGB8:
+			m_pencoding = new Block4x4Encoding_RGB8;
+			break;
+
+		case Image::Format::RGBA8:
+		case Image::Format::SRGBA8:
+			if (a_errormetric == RGBX)
+			{
+				m_pencoding = new Block4x4Encoding_RGBA8;
+			}
+			else
+			{
+				switch (m_sourcealphamix)
+				{
+				case SourceAlphaMix::OPAQUE:
+					m_pencoding = new Block4x4Encoding_RGBA8_Opaque;
+					break;
+
+				case SourceAlphaMix::TRANSPARENT:
+					m_pencoding = new Block4x4Encoding_RGBA8_Transparent;
+					break;
+
+				case SourceAlphaMix::TRANSLUCENT:
+					m_pencoding = new Block4x4Encoding_RGBA8;
+					break;
+
+				default:
+					assert(0);
+					break;
+				}
+				break;
+			}
+			break;
+
+		case Image::Format::RGB8A1:
+		case Image::Format::SRGB8A1:
+			switch (m_sourcealphamix)
+			{
+			case SourceAlphaMix::OPAQUE:
+				m_pencoding = new Block4x4Encoding_RGB8A1_Opaque;
+				break;
+
+			case SourceAlphaMix::TRANSPARENT:
+				m_pencoding = new Block4x4Encoding_RGB8A1_Transparent;
+				break;
+
+			case SourceAlphaMix::TRANSLUCENT:
+				if (m_boolPunchThroughPixels)
+				{
+					m_pencoding = new Block4x4Encoding_RGB8A1;
+				}
+				else
+				{
+					m_pencoding = new Block4x4Encoding_RGB8A1_Opaque;
+				}
+				break;
+
+			default:
+				assert(0);
+				break;
+			}
+			break;
+
+		case Image::Format::R11:
+		case Image::Format::SIGNED_R11:
+			m_pencoding = new Block4x4Encoding_R11;
+			break;
+		case Image::Format::RG11:
+		case Image::Format::SIGNED_RG11:
+			m_pencoding = new Block4x4Encoding_RG11;
+			break;
+		default:
+			assert(0);
+			break;
+		}
+
+		m_pencoding->InitFromSource(this, m_afrgbaSource,
+									a_paucEncodingBits, a_errormetric);
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// initialization of encoding state from a prior encoding using encoding bits
+	// [a_uiSourceH,a_uiSourceV] is the location of the block in a_pimageSource
+	// a_paucEncodingBits is the place to read the prior encoding
+	// a_imageformat is used to determine how to interpret a_paucEncodingBits
+	// a_errormetric was used for the prior encoding
+	//
+	void Block4x4::InitFromEtcEncodingBits(Image::Format a_imageformat,
+											unsigned int a_uiSourceH, unsigned int a_uiSourceV,
+											unsigned char *a_paucEncodingBits,
+											Image *a_pimageSource,
+											ErrorMetric a_errormetric)
+	{
+		Block4x4();
+
+		m_pimageSource = a_pimageSource;
+		m_uiSourceH = a_uiSourceH;
+		m_uiSourceV = a_uiSourceV;
+		m_errormetric = a_errormetric;
+
+		SetSourcePixels();
+
+		// set block encoder function
+		switch (a_imageformat)
+		{
+		case Image::Format::ETC1:
+			m_pencoding = new Block4x4Encoding_ETC1;
+			break;
+
+		case Image::Format::RGB8:
+		case Image::Format::SRGB8:
+			m_pencoding = new Block4x4Encoding_RGB8;
+			break;
+
+		case Image::Format::RGBA8:
+		case Image::Format::SRGBA8:
+			m_pencoding = new Block4x4Encoding_RGBA8;
+			break;
+
+		case Image::Format::RGB8A1:
+		case Image::Format::SRGB8A1:
+			m_pencoding = new Block4x4Encoding_RGB8A1;
+			break;
+
+		case Image::Format::R11:
+		case Image::Format::SIGNED_R11:
+			m_pencoding = new Block4x4Encoding_R11;
+			break;
+		case Image::Format::RG11:
+		case Image::Format::SIGNED_RG11:
+			m_pencoding = new Block4x4Encoding_RG11;
+			break;
+		default:
+			assert(0);
+			break;
+		}
+
+		m_pencoding->InitFromEncodingBits(this, a_paucEncodingBits, m_afrgbaSource,
+										m_pimageSource->GetErrorMetric());
+
+	}
+	
+	// ----------------------------------------------------------------------------------------------------
+	// set source pixels from m_pimageSource
+	// set m_alphamix
+	//
+	void Block4x4::SetSourcePixels(void)
+	{
+
+		Image::Format imageformat = m_pimageSource->GetFormat();
+
+		// alpha census
+		unsigned int uiTransparentSourcePixels = 0;
+		unsigned int uiOpaqueSourcePixels = 0;
+
+		// copy source to consecutive memory locations
+		// convert from image horizontal scan to block vertical scan
+		unsigned int uiPixel = 0;
+		for (unsigned int uiBlockPixelH = 0; uiBlockPixelH < Block4x4::COLUMNS; uiBlockPixelH++)
+		{
+			unsigned int uiSourcePixelH = m_uiSourceH + uiBlockPixelH;
+
+			for (unsigned int uiBlockPixelV = 0; uiBlockPixelV < Block4x4::ROWS; uiBlockPixelV++)
+			{
+				unsigned int uiSourcePixelV = m_uiSourceV + uiBlockPixelV;
+
+				ColorFloatRGBA *pfrgbaSource = m_pimageSource->GetSourcePixel(uiSourcePixelH, uiSourcePixelV);
+
+				// if pixel extends beyond source image because of block padding
+				if (pfrgbaSource == nullptr)
+				{
+					m_afrgbaSource[uiPixel] = ColorFloatRGBA(0.0f, 0.0f, 0.0f, NAN);	// denotes border pixel
+					m_boolBorderPixels = true;
+					uiTransparentSourcePixels++;
+				}
+				else
+				{
+					//get teh current pixel data, and store some of the attributes
+					//before capping values to fit the encoder type
+					
+					m_afrgbaSource[uiPixel] = (*pfrgbaSource).ClampRGBA();
+
+					if (m_afrgbaSource[uiPixel].fA == 1.0f || m_errormetric == RGBX)
+					{
+						m_pimageSource->m_iNumOpaquePixels++;
+					}
+					else if (m_afrgbaSource[uiPixel].fA == 0.0f)
+					{
+						m_pimageSource->m_iNumTransparentPixels++;
+					}
+					else if(m_afrgbaSource[uiPixel].fA > 0.0f && m_afrgbaSource[uiPixel].fA < 1.0f)
+					{
+						m_pimageSource->m_iNumTranslucentPixels++;
+					}
+					else
+					{
+						m_pimageSource->m_numOutOfRangeValues.fA++;
+					}
+
+					if (m_afrgbaSource[uiPixel].fR != 0.0f)
+					{
+						m_pimageSource->m_numColorValues.fR++;
+						//make sure we are getting a float between 0-1
+						if (m_afrgbaSource[uiPixel].fR - 1.0f > 0.0f)
+						{
+							m_pimageSource->m_numOutOfRangeValues.fR++;
+						}
+					}
+
+					if (m_afrgbaSource[uiPixel].fG != 0.0f)
+					{
+						m_pimageSource->m_numColorValues.fG++;
+						if (m_afrgbaSource[uiPixel].fG - 1.0f > 0.0f)
+						{
+							m_pimageSource->m_numOutOfRangeValues.fG++;
+						}
+					}
+					if (m_afrgbaSource[uiPixel].fB != 0.0f)
+					{
+						m_pimageSource->m_numColorValues.fB++;
+						if (m_afrgbaSource[uiPixel].fB - 1.0f > 0.0f)
+						{
+							m_pimageSource->m_numOutOfRangeValues.fB++;
+						}
+					}
+					// for formats with no alpha, set source alpha to 1
+					if (imageformat == Image::Format::ETC1 ||
+						imageformat == Image::Format::RGB8 ||
+						imageformat == Image::Format::SRGB8)
+					{
+						m_afrgbaSource[uiPixel].fA = 1.0f;
+					}
+
+					if (imageformat == Image::Format::R11 ||
+						imageformat == Image::Format::SIGNED_R11)
+					{
+						m_afrgbaSource[uiPixel].fA = 1.0f;
+						m_afrgbaSource[uiPixel].fG = 0.0f;
+						m_afrgbaSource[uiPixel].fB = 0.0f;
+					}
+
+					if (imageformat == Image::Format::RG11 ||
+						imageformat == Image::Format::SIGNED_RG11)
+					{
+						m_afrgbaSource[uiPixel].fA = 1.0f;
+						m_afrgbaSource[uiPixel].fB = 0.0f;
+					}
+
+				
+					// for RGB8A1, set source alpha to 0.0 or 1.0
+					// set punch through flag
+					if (imageformat == Image::Format::RGB8A1 ||
+						imageformat == Image::Format::SRGB8A1)
+					{
+						if (m_afrgbaSource[uiPixel].fA >= 0.5f)
+						{
+							m_afrgbaSource[uiPixel].fA = 1.0f;
+						}
+						else
+						{
+							m_afrgbaSource[uiPixel].fA = 0.0f;
+							m_boolPunchThroughPixels = true;
+						}
+					}
+
+					if (m_afrgbaSource[uiPixel].fA == 1.0f || m_errormetric == RGBX)
+					{
+						uiOpaqueSourcePixels++;
+					}
+					else if (m_afrgbaSource[uiPixel].fA == 0.0f)
+					{
+						uiTransparentSourcePixels++;
+					}
+
+				}
+
+				uiPixel += 1;
+			}
+		}
+
+		if (uiOpaqueSourcePixels == PIXELS)
+		{
+			m_sourcealphamix = SourceAlphaMix::OPAQUE;
+		}
+		else if (uiTransparentSourcePixels == PIXELS)
+		{
+			m_sourcealphamix = SourceAlphaMix::TRANSPARENT;
+		}
+		else
+		{
+			m_sourcealphamix = SourceAlphaMix::TRANSLUCENT;
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// return a name for the encoding mode
+	//
+	const char * Block4x4::GetEncodingModeName(void)
+	{
+
+		switch (m_pencoding->GetMode())
+		{
+		case Block4x4Encoding::MODE_ETC1:
+			return "ETC1";
+		case Block4x4Encoding::MODE_T:
+			return "T";
+		case Block4x4Encoding::MODE_H:
+			return "H";
+		case Block4x4Encoding::MODE_PLANAR:
+			return "PLANAR";
+		default:
+			return "???";
+		}
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+
+}

+ 172 - 0
thirdparty/etc2comp/EtcBlock4x4.h

@@ -0,0 +1,172 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "EtcColor.h"
+#include "EtcColorFloatRGBA.h"
+#include "EtcErrorMetric.h"
+#include "EtcImage.h"
+#include "EtcBlock4x4Encoding.h"
+
+namespace Etc
+{
+	class Block4x4EncodingBits;
+
+	class Block4x4
+	{
+	public:
+
+		static const unsigned int ROWS = 4;
+		static const unsigned int COLUMNS = 4;
+		static const unsigned int PIXELS = ROWS * COLUMNS;
+
+		// the alpha mix for a 4x4 block of pixels
+		enum class SourceAlphaMix
+		{
+			UNKNOWN,
+			//
+			OPAQUE,			// all 1.0
+			TRANSPARENT,	// all 0.0 or NAN
+			TRANSLUCENT		// not all opaque or transparent
+		};
+
+		typedef void (Block4x4::*EncoderFunctionPtr)(void);
+
+		Block4x4(void);
+		~Block4x4();
+		void InitFromSource(Image *a_pimageSource,
+							unsigned int a_uiSourceH,
+							unsigned int a_uiSourceV,
+							unsigned char *a_paucEncodingBits,
+							ErrorMetric a_errormetric);
+
+		void InitFromEtcEncodingBits(Image::Format a_imageformat,
+										unsigned int a_uiSourceH,
+										unsigned int a_uiSourceV,
+										unsigned char *a_paucEncodingBits,
+										Image *a_pimageSource,
+										ErrorMetric a_errormetric);
+
+		// return true if final iteration was performed
+		inline void PerformEncodingIteration(float a_fEffort)
+		{
+			m_pencoding->PerformIteration(a_fEffort);
+		}
+
+		inline void SetEncodingBitsFromEncoding(void)
+		{
+			m_pencoding->SetEncodingBits();
+		}
+
+		inline unsigned int GetSourceH(void)
+		{
+			return m_uiSourceH;
+		}
+
+		inline unsigned int GetSourceV(void)
+		{
+			return m_uiSourceV;
+		}
+
+		inline float GetError(void)
+		{
+			return m_pencoding->GetError();
+		}
+
+		static const unsigned int s_auiPixelOrderHScan[PIXELS];
+
+		inline ColorFloatRGBA * GetDecodedColors(void)
+		{
+			return m_pencoding->GetDecodedColors();
+		}
+
+		inline float * GetDecodedAlphas(void)
+		{
+			return m_pencoding->GetDecodedAlphas();
+		}
+
+		inline Block4x4Encoding::Mode GetEncodingMode(void)
+		{
+			return m_pencoding->GetMode();
+		}
+
+		inline bool GetFlip(void)
+		{
+			return m_pencoding->GetFlip();
+		}
+
+		inline bool IsDifferential(void)
+		{
+			return m_pencoding->IsDifferential();
+		}
+
+		inline ColorFloatRGBA * GetSource()
+		{
+			return m_afrgbaSource;
+		}
+
+		inline ErrorMetric GetErrorMetric()
+		{
+			return m_errormetric;
+		}
+
+		const char * GetEncodingModeName(void);
+
+		inline Block4x4Encoding * GetEncoding(void)
+		{
+			return m_pencoding;
+		}
+
+		inline SourceAlphaMix GetSourceAlphaMix(void)
+		{
+			return m_sourcealphamix;
+		}
+
+		inline Image * GetImageSource(void)
+		{
+			return m_pimageSource;
+		}
+
+		inline bool HasBorderPixels(void)
+		{
+			return m_boolBorderPixels;
+		}
+
+		inline bool HasPunchThroughPixels(void)
+		{
+			return m_boolPunchThroughPixels;
+		}
+
+	private:
+
+		void SetSourcePixels(void);
+
+		Image				*m_pimageSource;
+		unsigned int		m_uiSourceH;
+		unsigned int		m_uiSourceV;
+		ErrorMetric			m_errormetric;
+		ColorFloatRGBA		m_afrgbaSource[PIXELS];		// vertical scan
+
+		SourceAlphaMix		m_sourcealphamix;
+		bool				m_boolBorderPixels;			// marked as rgba(NAN, NAN, NAN, NAN)
+		bool				m_boolPunchThroughPixels;	// RGB8A1 or SRGB8A1 with any pixels with alpha < 0.5
+
+		Block4x4Encoding	*m_pencoding;
+
+	};
+
+} // namespace Etc

+ 261 - 0
thirdparty/etc2comp/EtcBlock4x4Encoding.cpp

@@ -0,0 +1,261 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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.
+ */
+
+/*
+EtcBlock4x4Encoding.cpp
+
+Block4x4Encoding is the abstract base class for the different encoders.  Each encoder targets a 
+particular file format (e.g. ETC1, RGB8, RGBA8, R11)
+
+*/
+
+#include "EtcConfig.h"
+#include "EtcBlock4x4Encoding.h"
+
+#include "EtcBlock4x4EncodingBits.h"
+#include "EtcBlock4x4.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+namespace Etc
+{
+	// ----------------------------------------------------------------------------------------------------
+	//
+	const float Block4x4Encoding::LUMA_WEIGHT = 3.0f;
+	const float Block4x4Encoding::CHROMA_BLUE_WEIGHT = 0.5f;
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+	Block4x4Encoding::Block4x4Encoding(void)
+	{
+
+		m_pblockParent = nullptr;
+
+		m_pafrgbaSource = nullptr;
+
+		m_boolBorderPixels = false;
+
+		m_fError = -1.0f;
+
+		m_mode = MODE_UNKNOWN;
+
+		m_uiEncodingIterations = 0;
+		m_boolDone = false;
+
+		for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+		{
+			m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA(-1.0f, -1.0f, -1.0f, -1.0f);
+			m_afDecodedAlphas[uiPixel] = -1.0f;
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// initialize the generic encoding for a 4x4 block
+	// a_pblockParent points to the block associated with this encoding
+	// a_errormetric is used to choose the best encoding
+	// init the decoded pixels to -1 to mark them as undefined
+	// init the error to -1 to mark it as undefined
+	//
+	void Block4x4Encoding::Init(Block4x4 *a_pblockParent,
+								ColorFloatRGBA *a_pafrgbaSource,
+								ErrorMetric a_errormetric)
+	{
+
+		m_pblockParent = a_pblockParent;
+
+		m_pafrgbaSource = a_pafrgbaSource;
+
+		m_boolBorderPixels = m_pblockParent->HasBorderPixels();
+
+		m_fError = -1.0f;
+
+		m_uiEncodingIterations = 0;
+
+		m_errormetric = a_errormetric;
+
+		for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+		{
+			m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA(-1.0f, -1.0f, -1.0f, -1.0f);
+			m_afDecodedAlphas[uiPixel] = -1.0f;
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// calculate the error for the block by summing the pixel errors
+	//
+	void Block4x4Encoding::CalcBlockError(void)
+	{
+		m_fError = 0.0f;
+
+		for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+		{
+			m_fError += CalcPixelError(m_afrgbaDecodedColors[uiPixel], m_afDecodedAlphas[uiPixel],
+										m_pafrgbaSource[uiPixel]);
+		}
+		
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// calculate the error between the source pixel and the decoded pixel
+	// the error amount is base on the error metric
+	//
+	float Block4x4Encoding::CalcPixelError(ColorFloatRGBA a_frgbaDecodedColor, float a_fDecodedAlpha,
+											ColorFloatRGBA a_frgbaSourcePixel)
+	{
+
+		// if a border pixel
+		if (isnan(a_frgbaSourcePixel.fA))
+		{
+			return 0.0f;
+		}
+
+		if (m_errormetric == ErrorMetric::RGBA)
+		{
+			assert(a_fDecodedAlpha >= 0.0f);
+
+			float fDRed = (a_fDecodedAlpha * a_frgbaDecodedColor.fR) -
+							(a_frgbaSourcePixel.fA * a_frgbaSourcePixel.fR);
+			float fDGreen = (a_fDecodedAlpha * a_frgbaDecodedColor.fG) -
+							(a_frgbaSourcePixel.fA * a_frgbaSourcePixel.fG);
+			float fDBlue = (a_fDecodedAlpha * a_frgbaDecodedColor.fB) -
+							(a_frgbaSourcePixel.fA * a_frgbaSourcePixel.fB);
+
+			float fDAlpha = a_fDecodedAlpha - a_frgbaSourcePixel.fA;
+
+			return fDRed*fDRed + fDGreen*fDGreen + fDBlue*fDBlue + fDAlpha*fDAlpha;
+		}
+		else if (m_errormetric == ErrorMetric::RGBX)
+		{
+			assert(a_fDecodedAlpha >= 0.0f);
+
+			float fDRed = a_frgbaDecodedColor.fR - a_frgbaSourcePixel.fR;
+			float fDGreen = a_frgbaDecodedColor.fG - a_frgbaSourcePixel.fG;
+			float fDBlue = a_frgbaDecodedColor.fB - a_frgbaSourcePixel.fB;
+			float fDAlpha = a_fDecodedAlpha - a_frgbaSourcePixel.fA;
+
+			return fDRed*fDRed + fDGreen*fDGreen + fDBlue*fDBlue + fDAlpha*fDAlpha;
+		}
+		else if (m_errormetric == ErrorMetric::REC709)
+		{
+			assert(a_fDecodedAlpha >= 0.0f);
+
+			float fLuma1 = a_frgbaSourcePixel.fR*0.2126f + a_frgbaSourcePixel.fG*0.7152f + a_frgbaSourcePixel.fB*0.0722f;
+			float fChromaR1 = 0.5f * ((a_frgbaSourcePixel.fR - fLuma1) * (1.0f / (1.0f - 0.2126f)));
+			float fChromaB1 = 0.5f * ((a_frgbaSourcePixel.fB - fLuma1) * (1.0f / (1.0f - 0.0722f)));
+
+			float fLuma2 = a_frgbaDecodedColor.fR*0.2126f +
+							a_frgbaDecodedColor.fG*0.7152f +
+							a_frgbaDecodedColor.fB*0.0722f;
+			float fChromaR2 = 0.5f * ((a_frgbaDecodedColor.fR - fLuma2) * (1.0f / (1.0f - 0.2126f)));
+			float fChromaB2 = 0.5f * ((a_frgbaDecodedColor.fB - fLuma2) * (1.0f / (1.0f - 0.0722f)));
+
+			float fDeltaL = a_frgbaSourcePixel.fA * fLuma1 - a_fDecodedAlpha * fLuma2;
+			float fDeltaCr = a_frgbaSourcePixel.fA * fChromaR1 - a_fDecodedAlpha * fChromaR2;
+			float fDeltaCb = a_frgbaSourcePixel.fA * fChromaB1 - a_fDecodedAlpha * fChromaB2;
+
+			float fDAlpha = a_fDecodedAlpha - a_frgbaSourcePixel.fA;
+
+			// Favor Luma accuracy over Chroma, and Red over Blue 
+			return LUMA_WEIGHT*fDeltaL*fDeltaL +
+					fDeltaCr*fDeltaCr +
+					CHROMA_BLUE_WEIGHT*fDeltaCb*fDeltaCb +
+					fDAlpha*fDAlpha;
+	#if 0
+			float fDRed = a_frgbaDecodedPixel.fR - a_frgbaSourcePixel.fR;
+			float fDGreen = a_frgbaDecodedPixel.fG - a_frgbaSourcePixel.fG;
+			float fDBlue = a_frgbaDecodedPixel.fB - a_frgbaSourcePixel.fB;
+			return 2.0f * 3.0f * fDeltaL * fDeltaL + fDRed*fDRed + fDGreen*fDGreen + fDBlue*fDBlue;
+#endif
+		}
+		else if (m_errormetric == ErrorMetric::NORMALXYZ)
+		{
+			float fDecodedX = 2.0f * a_frgbaDecodedColor.fR - 1.0f;
+			float fDecodedY = 2.0f * a_frgbaDecodedColor.fG - 1.0f;
+			float fDecodedZ = 2.0f * a_frgbaDecodedColor.fB - 1.0f;
+
+			float fDecodedLength = sqrtf(fDecodedX*fDecodedX + fDecodedY*fDecodedY + fDecodedZ*fDecodedZ);
+
+			if (fDecodedLength < 0.5f)
+			{
+				return 1.0f;
+			}
+			else if (fDecodedLength == 0.0f)
+			{
+				fDecodedX = 1.0f;
+				fDecodedY = 0.0f;
+				fDecodedZ = 0.0f;
+			}
+			else
+			{
+				fDecodedX /= fDecodedLength;
+				fDecodedY /= fDecodedLength;
+				fDecodedZ /= fDecodedLength;
+			}
+
+			float fSourceX = 2.0f * a_frgbaSourcePixel.fR - 1.0f;
+			float fSourceY = 2.0f * a_frgbaSourcePixel.fG - 1.0f;
+			float fSourceZ = 2.0f * a_frgbaSourcePixel.fB - 1.0f;
+
+			float fSourceLength = sqrtf(fSourceX*fSourceX + fSourceY*fSourceY + fSourceZ*fSourceZ);
+
+			if (fSourceLength == 0.0f)
+			{
+				fSourceX = 1.0f;
+				fSourceY = 0.0f;
+				fSourceZ = 0.0f;
+			}
+			else
+			{
+				fSourceX /= fSourceLength;
+				fSourceY /= fSourceLength;
+				fSourceZ /= fSourceLength;
+			}
+
+			float fDotProduct = fSourceX*fDecodedX + fSourceY*fDecodedY + fSourceZ*fDecodedZ;
+			float fNormalizedDotProduct = 1.0f - 0.5f * (fDotProduct + 1.0f);
+			float fDotProductError = fNormalizedDotProduct * fNormalizedDotProduct;
+			
+			float fLength2 = fDecodedX*fDecodedX + fDecodedY*fDecodedY + fDecodedZ*fDecodedZ;
+			float fLength2Error = fabsf(1.0f - fLength2);
+
+			float fDeltaW = a_frgbaDecodedColor.fA - a_frgbaSourcePixel.fA;
+			float fErrorW = fDeltaW * fDeltaW;
+
+			return fDotProductError + fLength2Error + fErrorW;
+		}
+		else // ErrorMetric::NUMERIC
+		{
+			assert(a_fDecodedAlpha >= 0.0f);
+
+			float fDX = a_frgbaDecodedColor.fR - a_frgbaSourcePixel.fR;
+			float fDY = a_frgbaDecodedColor.fG - a_frgbaSourcePixel.fG;
+			float fDZ = a_frgbaDecodedColor.fB - a_frgbaSourcePixel.fB;
+			float fDW = a_frgbaDecodedColor.fA - a_frgbaSourcePixel.fA;
+
+			return fDX*fDX + fDY*fDY + fDZ*fDZ + fDW*fDW;
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+
+} // namespace Etc
+

+ 148 - 0
thirdparty/etc2comp/EtcBlock4x4Encoding.h

@@ -0,0 +1,148 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "EtcColorFloatRGBA.h"
+
+#include "EtcErrorMetric.h"
+
+#include <assert.h>
+#include <float.h>
+
+namespace Etc
+{
+	class Block4x4;
+
+	// abstract base class for specific encodings
+	class Block4x4Encoding
+	{
+	public:
+
+		static const unsigned int ROWS = 4;
+		static const unsigned int COLUMNS = 4;
+		static const unsigned int PIXELS = ROWS * COLUMNS;
+		static const float LUMA_WEIGHT;
+		static const float CHROMA_BLUE_WEIGHT;
+
+		typedef enum
+		{
+			MODE_UNKNOWN,
+			//
+			MODE_ETC1,
+			MODE_T,
+			MODE_H,
+			MODE_PLANAR,
+			MODE_R11,
+			MODE_RG11,
+			//
+			MODES
+		} Mode;
+
+		Block4x4Encoding(void);
+		//virtual ~Block4x4Encoding(void) =0;
+		virtual ~Block4x4Encoding(void) {}
+		virtual void InitFromSource(Block4x4 *a_pblockParent,
+									ColorFloatRGBA *a_pafrgbaSource,
+
+									unsigned char *a_paucEncodingBits, ErrorMetric a_errormetric) = 0;
+
+		virtual void InitFromEncodingBits(Block4x4 *a_pblockParent,
+											unsigned char *a_paucEncodingBits,
+											ColorFloatRGBA *a_pafrgbaSource,
+
+											ErrorMetric a_errormetric) = 0;
+
+		// perform an iteration of the encoding
+		// the first iteration must generate a complete, valid (if poor) encoding
+		virtual void PerformIteration(float a_fEffort) = 0;
+
+		void CalcBlockError(void);
+
+		inline float GetError(void)
+		{
+			assert(m_fError >= 0.0f);
+
+			return m_fError;
+		}
+
+		inline ColorFloatRGBA * GetDecodedColors(void)
+		{
+			return m_afrgbaDecodedColors;
+		}
+
+		inline float * GetDecodedAlphas(void)
+		{
+			return m_afDecodedAlphas;
+		}
+
+		virtual void SetEncodingBits(void) = 0;
+
+		virtual bool GetFlip(void) = 0;
+
+		virtual bool IsDifferential(void) = 0;
+
+		virtual bool HasSeverelyBentDifferentialColors(void) const = 0;
+
+		inline Mode GetMode(void)
+		{
+			return m_mode;
+		}
+
+		inline bool IsDone(void)
+		{
+			return m_boolDone;
+		}
+
+		inline void SetDoneIfPerfect()
+		{
+			if (GetError() == 0.0f)
+			{
+				m_boolDone = true;
+			}
+		}
+
+		float CalcPixelError(ColorFloatRGBA a_frgbaDecodedColor, float a_fDecodedAlpha,
+								ColorFloatRGBA a_frgbaSourcePixel);
+
+	protected:
+
+		void Init(Block4x4 *a_pblockParent,
+					ColorFloatRGBA *a_pafrgbaSource,
+
+					ErrorMetric a_errormetric);
+
+		Block4x4		*m_pblockParent;
+		ColorFloatRGBA	*m_pafrgbaSource;
+
+		bool			m_boolBorderPixels;				// if block has any border pixels
+
+		ColorFloatRGBA	m_afrgbaDecodedColors[PIXELS];	// decoded RGB components, ignore Alpha
+		float			m_afDecodedAlphas[PIXELS];		// decoded alpha component
+		float			m_fError;						// error for RGBA relative to m_pafrgbaSource
+
+		// intermediate encoding
+		Mode			m_mode;
+
+		unsigned int	m_uiEncodingIterations;
+		bool			m_boolDone;						// all iterations have been done
+		ErrorMetric		m_errormetric;
+
+	private:
+
+	};
+
+} // namespace Etc

+ 315 - 0
thirdparty/etc2comp/EtcBlock4x4EncodingBits.h

@@ -0,0 +1,315 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <assert.h>
+
+namespace Etc
+{
+
+	// ################################################################################
+	// Block4x4EncodingBits
+	// Base class for Block4x4EncodingBits_XXXX
+	// ################################################################################
+
+	class Block4x4EncodingBits
+	{
+	public:
+
+		enum class Format
+		{
+			UNKNOWN,
+			//
+			RGB8,
+			RGBA8,
+			R11,
+			RG11,
+			RGB8A1,
+			//
+			FORMATS
+		};
+
+		static unsigned int GetBytesPerBlock(Format a_format)
+		{
+			switch (a_format)
+			{
+			case Format::RGB8:
+			case Format::R11:
+			case Format::RGB8A1:
+				return 8;
+				break;
+
+			case Format::RGBA8:
+			case Format::RG11:
+				return 16;
+				break;
+
+			default:
+				return 0;
+				break;
+			}
+
+		}
+
+	};
+
+	// ################################################################################
+	// Block4x4EncodingBits_RGB8
+	// Encoding bits for the RGB portion of ETC1, RGB8, RGB8A1 and RGBA8
+	// ################################################################################
+
+	class Block4x4EncodingBits_RGB8
+	{
+	public:
+
+		static const unsigned int BYTES_PER_BLOCK = 8;
+
+		inline Block4x4EncodingBits_RGB8(void)
+		{
+			assert(sizeof(Block4x4EncodingBits_RGB8) == BYTES_PER_BLOCK);
+
+			for (unsigned int uiByte = 0; uiByte < BYTES_PER_BLOCK; uiByte++)
+			{
+				auc[uiByte] = 0;
+			}
+
+		}
+
+		typedef struct
+		{
+			unsigned red2 : 4;
+			unsigned red1 : 4;
+			//
+			unsigned green2 : 4;
+			unsigned green1 : 4;
+			//
+			unsigned blue2 : 4;
+			unsigned blue1 : 4;
+			//
+			unsigned flip : 1;
+			unsigned diff : 1;
+			unsigned cw2 : 3;
+			unsigned cw1 : 3;
+			//
+			unsigned int selectors;
+		} Individual;
+
+		typedef struct
+		{
+			signed dred2 : 3;
+			unsigned red1 : 5;
+			//
+			signed dgreen2 : 3;
+			unsigned green1 : 5;
+			//
+			signed dblue2 : 3;
+			unsigned blue1 : 5;
+			//
+			unsigned flip : 1;
+			unsigned diff : 1;
+			unsigned cw2 : 3;
+			unsigned cw1 : 3;
+			//
+			unsigned int selectors;
+		} Differential;
+
+		typedef struct
+		{
+			unsigned red1b : 2;
+			unsigned detect2 : 1;
+			unsigned red1a : 2;
+			unsigned detect1 : 3;
+			//
+			unsigned blue1 : 4;
+			unsigned green1 : 4;
+			//
+			unsigned green2 : 4;
+			unsigned red2 : 4;
+			//
+			unsigned db : 1;
+			unsigned diff : 1;
+			unsigned da : 2;
+			unsigned blue2 : 4;
+			//
+			unsigned int selectors;
+		} T;
+
+		typedef struct
+		{
+			unsigned green1a : 3;
+			unsigned red1 : 4;
+			unsigned detect1 : 1;
+			//
+			unsigned blue1b : 2;
+			unsigned detect3 : 1;
+			unsigned blue1a : 1;
+			unsigned green1b : 1;
+			unsigned detect2 : 3;
+			//
+			unsigned green2a : 3;
+			unsigned red2 : 4;
+			unsigned blue1c : 1;
+			//
+			unsigned db : 1;
+			unsigned diff : 1;
+			unsigned da : 1;
+			unsigned blue2 : 4;
+			unsigned green2b : 1;
+			//
+			unsigned int selectors;
+		} H;
+
+		typedef struct
+		{
+			unsigned originGreen1 : 1;
+			unsigned originRed : 6;
+			unsigned detect1 : 1;
+			//
+			unsigned originBlue1 : 1;
+			unsigned originGreen2 : 6;
+			unsigned detect2 : 1;
+			//
+			unsigned originBlue3 : 2;
+			unsigned detect4 : 1;
+			unsigned originBlue2 : 2;
+			unsigned detect3 : 3;
+			//
+			unsigned horizRed2 : 1;
+			unsigned diff : 1;
+			unsigned horizRed1 : 5;
+			unsigned originBlue4 : 1;
+			//
+			unsigned horizBlue1: 1;
+			unsigned horizGreen : 7;
+			//
+			unsigned vertRed1 : 3;
+			unsigned horizBlue2 : 5;
+			//
+			unsigned vertGreen1 : 5;
+			unsigned vertRed2 : 3;
+			//
+			unsigned vertBlue : 6;
+			unsigned vertGreen2 : 2;
+		} Planar;
+
+		union
+		{
+			unsigned char auc[BYTES_PER_BLOCK];
+			unsigned long int ul;
+			Individual individual;
+			Differential differential;
+			T t;
+			H h;
+			Planar planar;
+		};
+
+	};
+
+	// ################################################################################
+	// Block4x4EncodingBits_A8
+	// Encoding bits for the A portion of RGBA8
+	// ################################################################################
+
+	class Block4x4EncodingBits_A8
+	{
+	public:
+
+		static const unsigned int BYTES_PER_BLOCK = 8;
+		static const unsigned int SELECTOR_BYTES = 6;
+
+		typedef struct
+		{
+			unsigned base : 8;
+			unsigned table : 4;
+			unsigned multiplier : 4;
+			unsigned selectors0 : 8;
+			unsigned selectors1 : 8;
+			unsigned selectors2 : 8;
+			unsigned selectors3 : 8;
+			unsigned selectors4 : 8;
+			unsigned selectors5 : 8;
+		} Data;
+
+		Data data;
+
+	};
+
+	// ################################################################################
+	// Block4x4EncodingBits_R11
+	// Encoding bits for the R portion of R11
+	// ################################################################################
+
+	class Block4x4EncodingBits_R11
+	{
+	public:
+
+		static const unsigned int BYTES_PER_BLOCK = 8;
+		static const unsigned int SELECTOR_BYTES = 6;
+
+		typedef struct
+		{
+			unsigned base : 8;
+			unsigned table : 4;
+			unsigned multiplier : 4;
+			unsigned selectors0 : 8;
+			unsigned selectors1 : 8;
+			unsigned selectors2 : 8;
+			unsigned selectors3 : 8;
+			unsigned selectors4 : 8;
+			unsigned selectors5 : 8;
+		} Data;
+
+		Data data;
+
+	};
+
+	class Block4x4EncodingBits_RG11
+	{
+	public:
+
+		static const unsigned int BYTES_PER_BLOCK = 16;
+		static const unsigned int SELECTOR_BYTES = 12;
+
+		typedef struct
+		{
+			//Red portion
+			unsigned baseR : 8;
+			unsigned tableIndexR : 4;
+			unsigned multiplierR : 4;
+			unsigned selectorsR0 : 8;
+			unsigned selectorsR1 : 8;
+			unsigned selectorsR2 : 8;
+			unsigned selectorsR3 : 8;
+			unsigned selectorsR4 : 8;
+			unsigned selectorsR5 : 8;
+			//Green portion
+			unsigned baseG : 8;
+			unsigned tableIndexG : 4;
+			unsigned multiplierG : 4;
+			unsigned selectorsG0 : 8;
+			unsigned selectorsG1 : 8;
+			unsigned selectorsG2 : 8;
+			unsigned selectorsG3 : 8;
+			unsigned selectorsG4 : 8;
+			unsigned selectorsG5 : 8;
+		} Data;
+
+		Data data;
+
+	};
+
+}

+ 1281 - 0
thirdparty/etc2comp/EtcBlock4x4Encoding_ETC1.cpp

@@ -0,0 +1,1281 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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.
+ */
+
+/*
+EtcBlock4x4Encoding_ETC1.cpp
+
+Block4x4Encoding_ETC1 is the encoder to use when targetting file format ETC1.  This encoder is also
+used for the ETC1 subset of file format RGB8, RGBA8 and RGB8A1
+
+*/
+
+#include "EtcConfig.h"
+#include "EtcBlock4x4Encoding_ETC1.h"
+
+#include "EtcBlock4x4.h"
+#include "EtcBlock4x4EncodingBits.h"
+#include "EtcDifferentialTrys.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <float.h>
+#include <limits>
+
+namespace Etc
+{
+
+	// pixel processing order if the flip bit = 0 (horizontal split)
+	const unsigned int Block4x4Encoding_ETC1::s_auiPixelOrderFlip0[PIXELS] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
+
+	// pixel processing order if the flip bit = 1 (vertical split)
+	const unsigned int Block4x4Encoding_ETC1::s_auiPixelOrderFlip1[PIXELS] = { 0, 1, 4, 5, 8, 9, 12, 13, 2, 3, 6, 7, 10, 11, 14, 15 };
+
+	// pixel processing order for horizontal scan (ETC normally does a vertical scan)
+	const unsigned int Block4x4Encoding_ETC1::s_auiPixelOrderHScan[PIXELS] = { 0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15 };
+
+	// pixel indices for different block halves
+	const unsigned int Block4x4Encoding_ETC1::s_auiLeftPixelMapping[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };
+	const unsigned int Block4x4Encoding_ETC1::s_auiRightPixelMapping[8] = { 8, 9, 10, 11, 12, 13, 14, 15 };
+	const unsigned int Block4x4Encoding_ETC1::s_auiTopPixelMapping[8] = { 0, 1, 4, 5, 8, 9, 12, 13 };
+	const unsigned int Block4x4Encoding_ETC1::s_auiBottomPixelMapping[8] = { 2, 3, 6, 7, 10, 11, 14, 15 };
+
+	// CW ranges that the ETC1 decoders use
+	// CW is basically a contrast for the different selector bits, since these values are offsets to the base color
+	// the first axis in the array is indexed by the CW in the encoding bits
+	// the second axis in the array is indexed by the selector bits
+	float Block4x4Encoding_ETC1::s_aafCwTable[CW_RANGES][SELECTORS] =
+	{
+		{ 2.0f / 255.0f, 8.0f / 255.0f, -2.0f / 255.0f, -8.0f / 255.0f },
+		{ 5.0f / 255.0f, 17.0f / 255.0f, -5.0f / 255.0f, -17.0f / 255.0f },
+		{ 9.0f / 255.0f, 29.0f / 255.0f, -9.0f / 255.0f, -29.0f / 255.0f },
+		{ 13.0f / 255.0f, 42.0f / 255.0f, -13.0f / 255.0f, -42.0f / 255.0f },
+		{ 18.0f / 255.0f, 60.0f / 255.0f, -18.0f / 255.0f, -60.0f / 255.0f },
+		{ 24.0f / 255.0f, 80.0f / 255.0f, -24.0f / 255.0f, -80.0f / 255.0f },
+		{ 33.0f / 255.0f, 106.0f / 255.0f, -33.0f / 255.0f, -106.0f / 255.0f },
+		{ 47.0f / 255.0f, 183.0f / 255.0f, -47.0f / 255.0f, -183.0f / 255.0f }
+	};
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+	Block4x4Encoding_ETC1::Block4x4Encoding_ETC1(void)
+	{
+		m_mode = MODE_ETC1;
+		m_boolDiff = false;
+		m_boolFlip = false;
+		m_frgbaColor1 = ColorFloatRGBA();
+		m_frgbaColor2 = ColorFloatRGBA();
+		m_uiCW1 = 0;
+		m_uiCW2 = 0;
+		for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+		{
+			m_auiSelectors[uiPixel] = 0;
+			m_afDecodedAlphas[uiPixel] = 1.0f;
+		}
+
+		m_boolMostLikelyFlip = false;
+
+		m_fError = -1.0f;
+
+		m_fError1 = -1.0f;
+		m_fError2 = -1.0f;
+		m_boolSeverelyBentDifferentialColors = false;
+
+		for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+		{
+			m_afDecodedAlphas[uiPixel] = 1.0f;
+		}
+
+	}
+
+	 Block4x4Encoding_ETC1::~Block4x4Encoding_ETC1(void) {}
+
+	// ----------------------------------------------------------------------------------------------------
+	// initialization prior to encoding
+	// a_pblockParent points to the block associated with this encoding
+	// a_errormetric is used to choose the best encoding
+	// a_pafrgbaSource points to a 4x4 block subset of the source image
+	// a_paucEncodingBits points to the final encoding bits
+	//
+	void Block4x4Encoding_ETC1::InitFromSource(Block4x4 *a_pblockParent,
+												ColorFloatRGBA *a_pafrgbaSource,
+												unsigned char *a_paucEncodingBits, ErrorMetric a_errormetric)
+	{
+
+		Block4x4Encoding::Init(a_pblockParent, a_pafrgbaSource,a_errormetric);
+
+		for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+		{
+			m_afDecodedAlphas[uiPixel] = 1.0f;
+		}
+
+		m_fError = -1.0f;
+
+		m_pencodingbitsRGB8 = (Block4x4EncodingBits_RGB8 *)(a_paucEncodingBits);
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// initialization from the encoding bits of a previous encoding
+	// a_pblockParent points to the block associated with this encoding
+	// a_errormetric is used to choose the best encoding
+	// a_pafrgbaSource points to a 4x4 block subset of the source image
+	// a_paucEncodingBits points to the final encoding bits of a previous encoding
+	//
+	void Block4x4Encoding_ETC1::InitFromEncodingBits(Block4x4 *a_pblockParent,
+														unsigned char *a_paucEncodingBits,
+														ColorFloatRGBA *a_pafrgbaSource, 
+														ErrorMetric a_errormetric)
+	{
+
+		Block4x4Encoding::Init(a_pblockParent, a_pafrgbaSource,a_errormetric);
+		m_fError = -1.0f;
+
+		m_pencodingbitsRGB8 = (Block4x4EncodingBits_RGB8 *)a_paucEncodingBits;
+
+		m_mode = MODE_ETC1;
+		m_boolDiff = m_pencodingbitsRGB8->individual.diff;
+		m_boolFlip = m_pencodingbitsRGB8->individual.flip;
+		if (m_boolDiff)
+		{
+			int iR2 = (int)(m_pencodingbitsRGB8->differential.red1 + m_pencodingbitsRGB8->differential.dred2);
+			if (iR2 < 0)
+			{
+				iR2 = 0;
+			}
+			else if (iR2 > 31)
+			{
+				iR2 = 31;
+			}
+
+			int iG2 = (int)(m_pencodingbitsRGB8->differential.green1 + m_pencodingbitsRGB8->differential.dgreen2);
+			if (iG2 < 0)
+			{
+				iG2 = 0;
+			}
+			else if (iG2 > 31)
+			{
+				iG2 = 31;
+			}
+
+			int iB2 = (int)(m_pencodingbitsRGB8->differential.blue1 + m_pencodingbitsRGB8->differential.dblue2);
+			if (iB2 < 0)
+			{
+				iB2 = 0;
+			}
+			else if (iB2 > 31)
+			{
+				iB2 = 31;
+			}
+
+			m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB5(m_pencodingbitsRGB8->differential.red1, m_pencodingbitsRGB8->differential.green1, m_pencodingbitsRGB8->differential.blue1);
+			m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB5((unsigned char)iR2, (unsigned char)iG2, (unsigned char)iB2);
+
+		}
+		else
+		{
+			m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4(m_pencodingbitsRGB8->individual.red1, m_pencodingbitsRGB8->individual.green1, m_pencodingbitsRGB8->individual.blue1);
+			m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4(m_pencodingbitsRGB8->individual.red2, m_pencodingbitsRGB8->individual.green2, m_pencodingbitsRGB8->individual.blue2);
+		}
+
+		m_uiCW1 = m_pencodingbitsRGB8->individual.cw1;
+		m_uiCW2 = m_pencodingbitsRGB8->individual.cw2;
+
+		InitFromEncodingBits_Selectors();
+
+		Decode();
+
+		CalcBlockError();
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// init the selectors from a prior encoding
+	//
+	void Block4x4Encoding_ETC1::InitFromEncodingBits_Selectors(void)
+	{
+
+		unsigned char *paucSelectors = (unsigned char *)&m_pencodingbitsRGB8->individual.selectors;
+
+		for (unsigned int iPixel = 0; iPixel < PIXELS; iPixel++)
+		{
+			unsigned int uiByteMSB = (unsigned int)(1 - (iPixel / 8));
+			unsigned int uiByteLSB = (unsigned int)(3 - (iPixel / 8));
+			unsigned int uiShift = (unsigned int)(iPixel & 7);
+
+			unsigned int uiSelectorMSB = (unsigned int)((paucSelectors[uiByteMSB] >> uiShift) & 1);
+			unsigned int uiSelectorLSB = (unsigned int)((paucSelectors[uiByteLSB] >> uiShift) & 1);
+
+			m_auiSelectors[iPixel] = (uiSelectorMSB << 1) + uiSelectorLSB;
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// perform a single encoding iteration
+	// replace the encoding if a better encoding was found
+	// subsequent iterations generally take longer for each iteration
+	// set m_boolDone if encoding is perfect or encoding is finished based on a_fEffort
+	//
+	void Block4x4Encoding_ETC1::PerformIteration(float a_fEffort)
+	{
+		assert(!m_boolDone);
+
+		switch (m_uiEncodingIterations)
+		{
+		case 0:
+			PerformFirstIteration();
+			break;
+
+		case 1:
+			TryDifferential(m_boolMostLikelyFlip, 1, 0, 0);
+			break;
+
+		case 2:
+			TryIndividual(m_boolMostLikelyFlip, 1);
+			if (a_fEffort <= 49.5f)
+			{
+				m_boolDone = true;
+			}
+			break;
+
+		case 3:
+			TryDifferential(!m_boolMostLikelyFlip, 1, 0, 0);
+			if (a_fEffort <= 59.5f)
+			{
+				m_boolDone = true;
+			}
+			break;
+
+		case 4:
+			TryIndividual(!m_boolMostLikelyFlip, 1);
+			if (a_fEffort <= 69.5f)
+			{
+				m_boolDone = true;
+			}
+			break;
+
+		case 5:
+			TryDegenerates1();
+			if (a_fEffort <= 79.5f)
+			{
+				m_boolDone = true;
+			}
+			break;
+
+		case 6:
+			TryDegenerates2();
+			if (a_fEffort <= 89.5f)
+			{
+				m_boolDone = true;
+			}
+			break;
+
+		case 7:
+			TryDegenerates3();
+			if (a_fEffort <= 99.5f)
+			{
+				m_boolDone = true;
+			}
+			break;
+
+		case 8:
+			TryDegenerates4();
+			m_boolDone = true;
+			break;
+
+		default:
+			assert(0);
+			break;
+		}
+
+		m_uiEncodingIterations++;
+		SetDoneIfPerfect();
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// find best initial encoding to ensure block has a valid encoding
+	//
+	void Block4x4Encoding_ETC1::PerformFirstIteration(void)
+	{
+		CalculateMostLikelyFlip();
+
+		m_fError = FLT_MAX;
+
+		TryDifferential(m_boolMostLikelyFlip, 0, 0, 0);
+		SetDoneIfPerfect();
+		if (m_boolDone)
+		{
+			return;
+		}
+
+		TryIndividual(m_boolMostLikelyFlip, 0);
+		SetDoneIfPerfect();
+		if (m_boolDone)
+		{
+			return;
+		}
+		TryDifferential(!m_boolMostLikelyFlip, 0, 0, 0);
+		SetDoneIfPerfect();
+		if (m_boolDone)
+		{
+			return;
+		}
+		TryIndividual(!m_boolMostLikelyFlip, 0);
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// algorithm:
+	// create a source average color for the Left, Right, Top and Bottom halves using the 8 pixels in each half
+	// note: the "gray line" is the line of equal delta RGB that goes thru the average color
+	// for each half:
+	//		see how close each of the 8 pixels are to the "gray line" that goes thru the source average color
+	//		create an error value that is the sum of the distances from the gray line
+	// h_error is the sum of Left and Right errors
+	// v_error is the sum of Top and Bottom errors
+	//
+	void Block4x4Encoding_ETC1::CalculateMostLikelyFlip(void)
+	{
+		static const bool DEBUG_PRINT = false;
+
+		CalculateSourceAverages();
+
+		float fLeftGrayErrorSum = 0.0f;
+		float fRightGrayErrorSum = 0.0f;
+		float fTopGrayErrorSum = 0.0f;
+		float fBottomGrayErrorSum = 0.0f;
+
+		for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++)
+		{
+			ColorFloatRGBA *pfrgbaLeft = &m_pafrgbaSource[uiPixel];
+			ColorFloatRGBA *pfrgbaRight = &m_pafrgbaSource[uiPixel + 8];
+			ColorFloatRGBA *pfrgbaTop = &m_pafrgbaSource[s_auiTopPixelMapping[uiPixel]];
+			ColorFloatRGBA *pfrgbaBottom = &m_pafrgbaSource[s_auiBottomPixelMapping[uiPixel]];
+
+			float fLeftGrayError = CalcGrayDistance2(*pfrgbaLeft, m_frgbaSourceAverageLeft);
+			float fRightGrayError = CalcGrayDistance2(*pfrgbaRight, m_frgbaSourceAverageRight);
+			float fTopGrayError = CalcGrayDistance2(*pfrgbaTop, m_frgbaSourceAverageTop);
+			float fBottomGrayError = CalcGrayDistance2(*pfrgbaBottom, m_frgbaSourceAverageBottom);
+
+			fLeftGrayErrorSum += fLeftGrayError;
+			fRightGrayErrorSum += fRightGrayError;
+			fTopGrayErrorSum += fTopGrayError;
+			fBottomGrayErrorSum += fBottomGrayError;
+		}
+
+		if (DEBUG_PRINT)
+		{
+			printf("\n%.2f %.2f\n", fLeftGrayErrorSum + fRightGrayErrorSum, fTopGrayErrorSum + fBottomGrayErrorSum);
+		}
+
+		m_boolMostLikelyFlip = (fTopGrayErrorSum + fBottomGrayErrorSum) < (fLeftGrayErrorSum + fRightGrayErrorSum);
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// calculate source pixel averages for each 2x2 quadrant in a 4x4 block
+	// these are used to determine the averages for each of the 4 different halves (left, right, top, bottom)
+	// ignore pixels that have alpha == NAN (these are border pixels outside of the source image)
+	// weight the averages based on a pixel's alpha
+	//
+	void Block4x4Encoding_ETC1::CalculateSourceAverages(void)
+	{
+		static const bool DEBUG_PRINT = false;
+
+		bool boolRGBX = m_pblockParent->GetImageSource()->GetErrorMetric() == ErrorMetric::RGBX;
+
+		if (m_pblockParent->GetSourceAlphaMix() == Block4x4::SourceAlphaMix::OPAQUE || boolRGBX)
+		{
+			ColorFloatRGBA frgbaSumUL = m_pafrgbaSource[0] + m_pafrgbaSource[1] + m_pafrgbaSource[4] + m_pafrgbaSource[5];
+			ColorFloatRGBA frgbaSumLL = m_pafrgbaSource[2] + m_pafrgbaSource[3] + m_pafrgbaSource[6] + m_pafrgbaSource[7];
+			ColorFloatRGBA frgbaSumUR = m_pafrgbaSource[8] + m_pafrgbaSource[9] + m_pafrgbaSource[12] + m_pafrgbaSource[13];
+			ColorFloatRGBA frgbaSumLR = m_pafrgbaSource[10] + m_pafrgbaSource[11] + m_pafrgbaSource[14] + m_pafrgbaSource[15];
+
+			m_frgbaSourceAverageLeft = (frgbaSumUL + frgbaSumLL) * 0.125f;
+			m_frgbaSourceAverageRight = (frgbaSumUR + frgbaSumLR) * 0.125f;
+			m_frgbaSourceAverageTop = (frgbaSumUL + frgbaSumUR) * 0.125f;
+			m_frgbaSourceAverageBottom = (frgbaSumLL + frgbaSumLR) * 0.125f;
+		}
+		else
+		{
+			float afSourceAlpha[PIXELS];
+
+			// treat alpha NAN as 0.0f
+			for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+			{
+				afSourceAlpha[uiPixel] = isnan(m_pafrgbaSource[uiPixel].fA) ? 
+																		0.0f : 
+																		m_pafrgbaSource[uiPixel].fA;
+			}
+
+			ColorFloatRGBA afrgbaAlphaWeightedSource[PIXELS];
+			for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+			{
+				afrgbaAlphaWeightedSource[uiPixel] = m_pafrgbaSource[uiPixel] * afSourceAlpha[uiPixel];
+			}
+
+			ColorFloatRGBA frgbaSumUL = afrgbaAlphaWeightedSource[0] +
+										afrgbaAlphaWeightedSource[1] +
+										afrgbaAlphaWeightedSource[4] +
+										afrgbaAlphaWeightedSource[5];
+
+			ColorFloatRGBA frgbaSumLL = afrgbaAlphaWeightedSource[2] +
+										afrgbaAlphaWeightedSource[3] +
+										afrgbaAlphaWeightedSource[6] +
+										afrgbaAlphaWeightedSource[7];
+
+			ColorFloatRGBA frgbaSumUR = afrgbaAlphaWeightedSource[8] +
+										afrgbaAlphaWeightedSource[9] +
+										afrgbaAlphaWeightedSource[12] +
+										afrgbaAlphaWeightedSource[13];
+
+			ColorFloatRGBA frgbaSumLR = afrgbaAlphaWeightedSource[10] +
+										afrgbaAlphaWeightedSource[11] +
+										afrgbaAlphaWeightedSource[14] +
+										afrgbaAlphaWeightedSource[15];
+
+			float fWeightSumUL = afSourceAlpha[0] +
+									afSourceAlpha[1] +
+									afSourceAlpha[4] +
+									afSourceAlpha[5];
+
+			float fWeightSumLL = afSourceAlpha[2] +
+									afSourceAlpha[3] +
+									afSourceAlpha[6] +
+									afSourceAlpha[7];
+
+			float fWeightSumUR = afSourceAlpha[8] +
+									afSourceAlpha[9] +
+									afSourceAlpha[12] +
+									afSourceAlpha[13];
+
+			float fWeightSumLR = afSourceAlpha[10] +
+									afSourceAlpha[11] +
+									afSourceAlpha[14] +
+									afSourceAlpha[15];
+
+			ColorFloatRGBA frgbaSumLeft = frgbaSumUL + frgbaSumLL;
+			ColorFloatRGBA frgbaSumRight = frgbaSumUR + frgbaSumLR;
+			ColorFloatRGBA frgbaSumTop = frgbaSumUL + frgbaSumUR;
+			ColorFloatRGBA frgbaSumBottom = frgbaSumLL + frgbaSumLR;
+
+			float fWeightSumLeft = fWeightSumUL + fWeightSumLL;
+			float fWeightSumRight = fWeightSumUR + fWeightSumLR;
+			float fWeightSumTop = fWeightSumUL + fWeightSumUR;
+			float fWeightSumBottom = fWeightSumLL + fWeightSumLR;
+
+			// check to see if there is at least 1 pixel with  non-zero alpha
+			// completely transparent block should not make it to this code
+			assert((fWeightSumLeft + fWeightSumRight) > 0.0f);
+			assert((fWeightSumTop + fWeightSumBottom) > 0.0f);
+
+			if (fWeightSumLeft > 0.0f)
+			{
+				m_frgbaSourceAverageLeft = frgbaSumLeft * (1.0f/fWeightSumLeft);
+			}
+			if (fWeightSumRight > 0.0f)
+			{
+				m_frgbaSourceAverageRight = frgbaSumRight * (1.0f/fWeightSumRight);
+			}
+			if (fWeightSumTop > 0.0f)
+			{
+				m_frgbaSourceAverageTop = frgbaSumTop * (1.0f/fWeightSumTop);
+			}
+			if (fWeightSumBottom > 0.0f)
+			{
+				m_frgbaSourceAverageBottom = frgbaSumBottom * (1.0f/fWeightSumBottom);
+			}
+
+			if (fWeightSumLeft == 0.0f)
+			{
+				assert(fWeightSumRight > 0.0f);
+				m_frgbaSourceAverageLeft = m_frgbaSourceAverageRight;
+			}
+			if (fWeightSumRight == 0.0f)
+			{
+				assert(fWeightSumLeft > 0.0f);
+				m_frgbaSourceAverageRight = m_frgbaSourceAverageLeft;
+			}
+			if (fWeightSumTop == 0.0f)
+			{
+				assert(fWeightSumBottom > 0.0f);
+				m_frgbaSourceAverageTop = m_frgbaSourceAverageBottom;
+			}
+			if (fWeightSumBottom == 0.0f)
+			{
+				assert(fWeightSumTop > 0.0f);
+				m_frgbaSourceAverageBottom = m_frgbaSourceAverageTop;
+			}
+		}
+
+		
+
+		if (DEBUG_PRINT)
+		{
+			printf("\ntarget: [%.2f,%.2f,%.2f] [%.2f,%.2f,%.2f] [%.2f,%.2f,%.2f] [%.2f,%.2f,%.2f]\n",
+				m_frgbaSourceAverageLeft.fR, m_frgbaSourceAverageLeft.fG, m_frgbaSourceAverageLeft.fB,
+				m_frgbaSourceAverageRight.fR, m_frgbaSourceAverageRight.fG, m_frgbaSourceAverageRight.fB,
+				m_frgbaSourceAverageTop.fR, m_frgbaSourceAverageTop.fG, m_frgbaSourceAverageTop.fB,
+				m_frgbaSourceAverageBottom.fR, m_frgbaSourceAverageBottom.fG, m_frgbaSourceAverageBottom.fB);
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// try an ETC1 differential mode encoding
+	// use a_boolFlip to set the encoding F bit
+	// use a_uiRadius to alter basecolor components in the range[-a_uiRadius:a_uiRadius]
+	// use a_iGrayOffset1 and a_iGrayOffset2 to offset the basecolor to search for degenerate encodings
+	// replace the encoding if the encoding error is less than previous encoding
+	//
+	void Block4x4Encoding_ETC1::TryDifferential(bool a_boolFlip, unsigned int a_uiRadius,
+												int a_iGrayOffset1, int a_iGrayOffset2)
+	{
+
+		ColorFloatRGBA frgbaColor1;
+		ColorFloatRGBA frgbaColor2;
+
+		const unsigned int *pauiPixelMapping1;
+		const unsigned int *pauiPixelMapping2;
+
+		if (a_boolFlip)
+		{
+			frgbaColor1 = m_frgbaSourceAverageTop;
+			frgbaColor2 = m_frgbaSourceAverageBottom;
+
+			pauiPixelMapping1 = s_auiTopPixelMapping;
+			pauiPixelMapping2 = s_auiBottomPixelMapping;
+		}
+		else
+		{
+			frgbaColor1 = m_frgbaSourceAverageLeft;
+			frgbaColor2 = m_frgbaSourceAverageRight;
+
+			pauiPixelMapping1 = s_auiLeftPixelMapping;
+			pauiPixelMapping2 = s_auiRightPixelMapping;
+		}
+
+		DifferentialTrys trys(frgbaColor1, frgbaColor2, pauiPixelMapping1, pauiPixelMapping2, 
+								a_uiRadius, a_iGrayOffset1, a_iGrayOffset2);
+
+		Block4x4Encoding_ETC1 encodingTry = *this;
+		encodingTry.m_boolFlip = a_boolFlip;
+
+		encodingTry.TryDifferentialHalf(&trys.m_half1);
+		encodingTry.TryDifferentialHalf(&trys.m_half2);
+
+		// find best halves that are within differential range
+		DifferentialTrys::Try *ptryBest1 = nullptr;
+		DifferentialTrys::Try *ptryBest2 = nullptr;
+		encodingTry.m_fError = FLT_MAX;
+
+		// see if the best of each half are in differential range
+		int iDRed = trys.m_half2.m_ptryBest->m_iRed - trys.m_half1.m_ptryBest->m_iRed;
+		int iDGreen = trys.m_half2.m_ptryBest->m_iGreen - trys.m_half1.m_ptryBest->m_iGreen;
+		int iDBlue = trys.m_half2.m_ptryBest->m_iBlue - trys.m_half1.m_ptryBest->m_iBlue;
+		if (iDRed >= -4 && iDRed <= 3 && iDGreen >= -4 && iDGreen <= 3 && iDBlue >= -4 && iDBlue <= 3)
+		{
+			ptryBest1 = trys.m_half1.m_ptryBest;
+			ptryBest2 = trys.m_half2.m_ptryBest;
+			encodingTry.m_fError = trys.m_half1.m_ptryBest->m_fError + trys.m_half2.m_ptryBest->m_fError;
+		}
+		else
+		{
+			// else, find the next best halves that are in differential range
+			for (DifferentialTrys::Try *ptry1 = &trys.m_half1.m_atry[0];
+			ptry1 < &trys.m_half1.m_atry[trys.m_half1.m_uiTrys];
+				ptry1++)
+			{
+				for (DifferentialTrys::Try *ptry2 = &trys.m_half2.m_atry[0];
+				ptry2 < &trys.m_half2.m_atry[trys.m_half2.m_uiTrys];
+					ptry2++)
+				{
+					iDRed = ptry2->m_iRed - ptry1->m_iRed;
+					bool boolValidRedDelta = iDRed <= 3 && iDRed >= -4;
+					iDGreen = ptry2->m_iGreen - ptry1->m_iGreen;
+					bool boolValidGreenDelta = iDGreen <= 3 && iDGreen >= -4;
+					iDBlue = ptry2->m_iBlue - ptry1->m_iBlue;
+					bool boolValidBlueDelta = iDBlue <= 3 && iDBlue >= -4;
+
+					if (boolValidRedDelta && boolValidGreenDelta && boolValidBlueDelta)
+					{
+						float fError = ptry1->m_fError + ptry2->m_fError;
+
+						if (fError < encodingTry.m_fError)
+						{
+							encodingTry.m_fError = fError;
+
+							ptryBest1 = ptry1;
+							ptryBest2 = ptry2;
+						}
+					}
+
+				}
+			}
+			assert(encodingTry.m_fError < FLT_MAX);
+			assert(ptryBest1 != nullptr);
+			assert(ptryBest2 != nullptr);
+		}
+
+		if (encodingTry.m_fError < m_fError)
+		{
+			m_mode = MODE_ETC1;
+			m_boolDiff = true;
+			m_boolFlip = encodingTry.m_boolFlip;
+			m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB5((unsigned char)ptryBest1->m_iRed, (unsigned char)ptryBest1->m_iGreen, (unsigned char)ptryBest1->m_iBlue);
+			m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB5((unsigned char)ptryBest2->m_iRed, (unsigned char)ptryBest2->m_iGreen, (unsigned char)ptryBest2->m_iBlue);
+			m_uiCW1 = ptryBest1->m_uiCW;
+			m_uiCW2 = ptryBest2->m_uiCW;
+
+			for (unsigned int uiPixelOrder = 0; uiPixelOrder < PIXELS / 2; uiPixelOrder++)
+			{
+				unsigned int uiPixel1 = pauiPixelMapping1[uiPixelOrder];
+				unsigned int uiPixel2 = pauiPixelMapping2[uiPixelOrder];
+
+				unsigned int uiSelector1 = ptryBest1->m_auiSelectors[uiPixelOrder];
+				unsigned int uiSelector2 = ptryBest2->m_auiSelectors[uiPixelOrder];
+
+				m_auiSelectors[uiPixel1] = uiSelector1;
+				m_auiSelectors[uiPixel2] = ptryBest2->m_auiSelectors[uiPixelOrder];
+
+				float fDeltaRGB1 = s_aafCwTable[m_uiCW1][uiSelector1];
+				float fDeltaRGB2 = s_aafCwTable[m_uiCW2][uiSelector2];
+
+				m_afrgbaDecodedColors[uiPixel1] = (m_frgbaColor1 + fDeltaRGB1).ClampRGB();
+				m_afrgbaDecodedColors[uiPixel2] = (m_frgbaColor2 + fDeltaRGB2).ClampRGB();
+			}
+
+			m_fError1 = ptryBest1->m_fError;
+			m_fError2 = ptryBest2->m_fError;
+			m_boolSeverelyBentDifferentialColors = trys.m_boolSeverelyBentColors;
+			m_fError = m_fError1 + m_fError2;
+
+			// sanity check
+			{
+				int iRed1 = m_frgbaColor1.IntRed(31.0f);
+				int iGreen1 = m_frgbaColor1.IntGreen(31.0f);
+				int iBlue1 = m_frgbaColor1.IntBlue(31.0f);
+
+				int iRed2 = m_frgbaColor2.IntRed(31.0f);
+				int iGreen2 = m_frgbaColor2.IntGreen(31.0f);
+				int iBlue2 = m_frgbaColor2.IntBlue(31.0f);
+
+				iDRed = iRed2 - iRed1;
+				iDGreen = iGreen2 - iGreen1;
+				iDBlue = iBlue2 - iBlue1;
+
+				assert(iDRed >= -4 && iDRed < 4);
+				assert(iDGreen >= -4 && iDGreen < 4);
+				assert(iDBlue >= -4 && iDBlue < 4);
+			}
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// try an ETC1 differential mode encoding for a half of a 4x4 block
+	// vary the basecolor components using a radius
+	//
+	void Block4x4Encoding_ETC1::TryDifferentialHalf(DifferentialTrys::Half *a_phalf)
+	{
+
+		a_phalf->m_ptryBest = nullptr;
+		float fBestTryError = FLT_MAX;
+
+		a_phalf->m_uiTrys = 0;
+		for (int iRed = a_phalf->m_iRed - (int)a_phalf->m_uiRadius; 
+				iRed <= a_phalf->m_iRed + (int)a_phalf->m_uiRadius;
+				iRed++)
+		{
+			assert(iRed >= 0 && iRed <= 31);
+
+			for (int iGreen = a_phalf->m_iGreen - (int)a_phalf->m_uiRadius;
+					iGreen <= a_phalf->m_iGreen + (int)a_phalf->m_uiRadius;
+					iGreen++)
+			{
+				assert(iGreen >= 0 && iGreen <= 31);
+
+				for (int iBlue = a_phalf->m_iBlue - (int)a_phalf->m_uiRadius;
+						iBlue <= a_phalf->m_iBlue + (int)a_phalf->m_uiRadius;
+						iBlue++)
+				{
+					assert(iBlue >= 0 && iBlue <= 31);
+
+					DifferentialTrys::Try *ptry = &a_phalf->m_atry[a_phalf->m_uiTrys];
+					assert(ptry < &a_phalf->m_atry[DifferentialTrys::Half::MAX_TRYS]);
+
+					ptry->m_iRed = iRed;
+					ptry->m_iGreen = iGreen;
+					ptry->m_iBlue = iBlue;
+					ptry->m_fError = FLT_MAX;
+					ColorFloatRGBA frgbaColor = ColorFloatRGBA::ConvertFromRGB5((unsigned char)iRed, (unsigned char)iGreen, (unsigned char)iBlue);
+
+					// try each CW
+					for (unsigned int uiCW = 0; uiCW < CW_RANGES; uiCW++)
+					{
+						unsigned int auiPixelSelectors[PIXELS / 2];
+						ColorFloatRGBA	afrgbaDecodedPixels[PIXELS / 2];
+						float afPixelErrors[PIXELS / 2] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, 
+															FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX };
+
+						// pre-compute decoded pixels for each selector
+						ColorFloatRGBA afrgbaSelectors[SELECTORS];
+						assert(SELECTORS == 4);
+						afrgbaSelectors[0] = (frgbaColor + s_aafCwTable[uiCW][0]).ClampRGB();
+						afrgbaSelectors[1] = (frgbaColor + s_aafCwTable[uiCW][1]).ClampRGB();
+						afrgbaSelectors[2] = (frgbaColor + s_aafCwTable[uiCW][2]).ClampRGB();
+						afrgbaSelectors[3] = (frgbaColor + s_aafCwTable[uiCW][3]).ClampRGB();
+
+						for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++)
+						{
+							ColorFloatRGBA *pfrgbaSourcePixel = &m_pafrgbaSource[a_phalf->m_pauiPixelMapping[uiPixel]];
+							ColorFloatRGBA frgbaDecodedPixel;
+
+							for (unsigned int uiSelector = 0; uiSelector < SELECTORS; uiSelector++)
+							{
+								frgbaDecodedPixel = afrgbaSelectors[uiSelector];
+
+								float fPixelError;
+
+								fPixelError = CalcPixelError(frgbaDecodedPixel, m_afDecodedAlphas[a_phalf->m_pauiPixelMapping[uiPixel]],
+																	*pfrgbaSourcePixel);
+
+								if (fPixelError < afPixelErrors[uiPixel])
+								{
+									auiPixelSelectors[uiPixel] = uiSelector;
+									afrgbaDecodedPixels[uiPixel] = frgbaDecodedPixel;
+									afPixelErrors[uiPixel] = fPixelError;
+								}
+
+							}
+						}
+
+						// add up all pixel errors
+						float fCWError = 0.0f;
+						for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++)
+						{	
+							fCWError += afPixelErrors[uiPixel];
+						}
+
+						// if best CW so far
+						if (fCWError < ptry->m_fError)
+						{
+							ptry->m_uiCW = uiCW;
+							for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++)
+							{
+								ptry->m_auiSelectors[uiPixel] = auiPixelSelectors[uiPixel];
+							}
+							ptry->m_fError = fCWError;
+						}
+
+					}
+
+					if (ptry->m_fError < fBestTryError)
+					{
+						a_phalf->m_ptryBest = ptry;
+						fBestTryError = ptry->m_fError;
+					}
+
+					assert(ptry->m_fError < FLT_MAX);
+
+					a_phalf->m_uiTrys++;
+				}
+			}
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// try an ETC1 individual mode encoding
+	// use a_boolFlip to set the encoding F bit
+	// use a_uiRadius to alter basecolor components in the range[-a_uiRadius:a_uiRadius]
+	// replace the encoding if the encoding error is less than previous encoding
+	//
+	void Block4x4Encoding_ETC1::TryIndividual(bool a_boolFlip, unsigned int a_uiRadius)
+	{
+
+		ColorFloatRGBA frgbaColor1;
+		ColorFloatRGBA frgbaColor2;
+
+		const unsigned int *pauiPixelMapping1;
+		const unsigned int *pauiPixelMapping2;
+
+		if (a_boolFlip)
+		{
+			frgbaColor1 = m_frgbaSourceAverageTop;
+			frgbaColor2 = m_frgbaSourceAverageBottom;
+
+			pauiPixelMapping1 = s_auiTopPixelMapping;
+			pauiPixelMapping2 = s_auiBottomPixelMapping;
+		}
+		else
+		{
+			frgbaColor1 = m_frgbaSourceAverageLeft;
+			frgbaColor2 = m_frgbaSourceAverageRight;
+
+			pauiPixelMapping1 = s_auiLeftPixelMapping;
+			pauiPixelMapping2 = s_auiRightPixelMapping;
+		}
+
+		IndividualTrys trys(frgbaColor1, frgbaColor2, pauiPixelMapping1, pauiPixelMapping2, a_uiRadius);
+
+		Block4x4Encoding_ETC1 encodingTry = *this;
+		encodingTry.m_boolFlip = a_boolFlip;
+
+		encodingTry.TryIndividualHalf(&trys.m_half1);
+		encodingTry.TryIndividualHalf(&trys.m_half2);
+
+		// use the best of each half
+		IndividualTrys::Try *ptryBest1 = trys.m_half1.m_ptryBest;
+		IndividualTrys::Try *ptryBest2 = trys.m_half2.m_ptryBest;
+		encodingTry.m_fError = trys.m_half1.m_ptryBest->m_fError + trys.m_half2.m_ptryBest->m_fError;
+
+		if (encodingTry.m_fError < m_fError)
+		{
+			m_mode = MODE_ETC1;
+			m_boolDiff = false;
+			m_boolFlip = encodingTry.m_boolFlip;
+			m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)ptryBest1->m_iRed, (unsigned char)ptryBest1->m_iGreen, (unsigned char)ptryBest1->m_iBlue);
+			m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)ptryBest2->m_iRed, (unsigned char)ptryBest2->m_iGreen, (unsigned char)ptryBest2->m_iBlue);
+			m_uiCW1 = ptryBest1->m_uiCW;
+			m_uiCW2 = ptryBest2->m_uiCW;
+
+			for (unsigned int uiPixelOrder = 0; uiPixelOrder < PIXELS / 2; uiPixelOrder++)
+			{
+				unsigned int uiPixel1 = pauiPixelMapping1[uiPixelOrder];
+				unsigned int uiPixel2 = pauiPixelMapping2[uiPixelOrder];
+
+				unsigned int uiSelector1 = ptryBest1->m_auiSelectors[uiPixelOrder];
+				unsigned int uiSelector2 = ptryBest2->m_auiSelectors[uiPixelOrder];
+
+				m_auiSelectors[uiPixel1] = uiSelector1;
+				m_auiSelectors[uiPixel2] = ptryBest2->m_auiSelectors[uiPixelOrder];
+
+				float fDeltaRGB1 = s_aafCwTable[m_uiCW1][uiSelector1];
+				float fDeltaRGB2 = s_aafCwTable[m_uiCW2][uiSelector2];
+
+				m_afrgbaDecodedColors[uiPixel1] = (m_frgbaColor1 + fDeltaRGB1).ClampRGB();
+				m_afrgbaDecodedColors[uiPixel2] = (m_frgbaColor2 + fDeltaRGB2).ClampRGB();
+			}
+
+			m_fError1 = ptryBest1->m_fError;
+			m_fError2 = ptryBest2->m_fError;
+			m_fError = m_fError1 + m_fError2;
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// try an ETC1 differential mode encoding for a half of a 4x4 block
+	// vary the basecolor components using a radius
+	//
+	void Block4x4Encoding_ETC1::TryIndividualHalf(IndividualTrys::Half *a_phalf)
+	{
+
+		a_phalf->m_ptryBest = nullptr;
+		float fBestTryError = FLT_MAX;
+
+		a_phalf->m_uiTrys = 0;
+		for (int iRed = a_phalf->m_iRed - (int)a_phalf->m_uiRadius;
+			iRed <= a_phalf->m_iRed + (int)a_phalf->m_uiRadius;
+			iRed++)
+		{
+			assert(iRed >= 0 && iRed <= 15);
+
+			for (int iGreen = a_phalf->m_iGreen - (int)a_phalf->m_uiRadius;
+				iGreen <= a_phalf->m_iGreen + (int)a_phalf->m_uiRadius;
+				iGreen++)
+			{
+				assert(iGreen >= 0 && iGreen <= 15);
+
+				for (int iBlue = a_phalf->m_iBlue - (int)a_phalf->m_uiRadius;
+					iBlue <= a_phalf->m_iBlue + (int)a_phalf->m_uiRadius;
+					iBlue++)
+				{
+					assert(iBlue >= 0 && iBlue <= 15);
+
+					IndividualTrys::Try *ptry = &a_phalf->m_atry[a_phalf->m_uiTrys];
+					assert(ptry < &a_phalf->m_atry[IndividualTrys::Half::MAX_TRYS]);
+
+					ptry->m_iRed = iRed;
+					ptry->m_iGreen = iGreen;
+					ptry->m_iBlue = iBlue;
+					ptry->m_fError = FLT_MAX;
+					ColorFloatRGBA frgbaColor = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed, (unsigned char)iGreen, (unsigned char)iBlue);
+
+					// try each CW
+					for (unsigned int uiCW = 0; uiCW < CW_RANGES; uiCW++)
+					{
+						unsigned int auiPixelSelectors[PIXELS / 2];
+						ColorFloatRGBA	afrgbaDecodedPixels[PIXELS / 2];
+						float afPixelErrors[PIXELS / 2] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX,
+															FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX };
+
+						// pre-compute decoded pixels for each selector
+						ColorFloatRGBA afrgbaSelectors[SELECTORS];
+						assert(SELECTORS == 4);
+						afrgbaSelectors[0] = (frgbaColor + s_aafCwTable[uiCW][0]).ClampRGB();
+						afrgbaSelectors[1] = (frgbaColor + s_aafCwTable[uiCW][1]).ClampRGB();
+						afrgbaSelectors[2] = (frgbaColor + s_aafCwTable[uiCW][2]).ClampRGB();
+						afrgbaSelectors[3] = (frgbaColor + s_aafCwTable[uiCW][3]).ClampRGB();
+
+						for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++)
+						{
+							ColorFloatRGBA *pfrgbaSourcePixel = &m_pafrgbaSource[a_phalf->m_pauiPixelMapping[uiPixel]];
+							ColorFloatRGBA frgbaDecodedPixel;
+
+							for (unsigned int uiSelector = 0; uiSelector < SELECTORS; uiSelector++)
+							{
+								frgbaDecodedPixel = afrgbaSelectors[uiSelector];
+
+								float fPixelError;
+
+								fPixelError = CalcPixelError(frgbaDecodedPixel, m_afDecodedAlphas[a_phalf->m_pauiPixelMapping[uiPixel]],
+										*pfrgbaSourcePixel);
+
+								if (fPixelError < afPixelErrors[uiPixel])
+								{
+									auiPixelSelectors[uiPixel] = uiSelector;
+									afrgbaDecodedPixels[uiPixel] = frgbaDecodedPixel;
+									afPixelErrors[uiPixel] = fPixelError;
+								}
+
+							}
+						}
+
+						// add up all pixel errors
+						float fCWError = 0.0f;
+						for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++)
+						{
+							fCWError += afPixelErrors[uiPixel];
+						}
+
+						// if best CW so far
+						if (fCWError < ptry->m_fError)
+						{
+							ptry->m_uiCW = uiCW;
+							for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++)
+							{
+								ptry->m_auiSelectors[uiPixel] = auiPixelSelectors[uiPixel];
+							}
+							ptry->m_fError = fCWError;
+						}
+
+					}
+
+					if (ptry->m_fError < fBestTryError)
+					{
+						a_phalf->m_ptryBest = ptry;
+						fBestTryError = ptry->m_fError;
+					}
+
+					assert(ptry->m_fError < FLT_MAX);
+
+					a_phalf->m_uiTrys++;
+				}
+			}
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// try version 1 of the degenerate search
+	// degenerate encodings use basecolor movement and a subset of the selectors to find useful encodings
+	// each subsequent version of the degenerate search uses more basecolor movement and is less likely to
+	//		be successfull
+	//
+	void Block4x4Encoding_ETC1::TryDegenerates1(void)
+	{
+
+		TryDifferential(m_boolMostLikelyFlip, 1, -2, 0);
+		TryDifferential(m_boolMostLikelyFlip, 1, 2, 0);
+		TryDifferential(m_boolMostLikelyFlip, 1, 0, 2);
+		TryDifferential(m_boolMostLikelyFlip, 1, 0, -2);
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// try version 2 of the degenerate search
+	// degenerate encodings use basecolor movement and a subset of the selectors to find useful encodings
+	// each subsequent version of the degenerate search uses more basecolor movement and is less likely to
+	//		be successfull
+	//
+	void Block4x4Encoding_ETC1::TryDegenerates2(void)
+	{
+
+		TryDifferential(!m_boolMostLikelyFlip, 1, -2, 0);
+		TryDifferential(!m_boolMostLikelyFlip, 1, 2, 0);
+		TryDifferential(!m_boolMostLikelyFlip, 1, 0, 2);
+		TryDifferential(!m_boolMostLikelyFlip, 1, 0, -2);
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// try version 3 of the degenerate search
+	// degenerate encodings use basecolor movement and a subset of the selectors to find useful encodings
+	// each subsequent version of the degenerate search uses more basecolor movement and is less likely to
+	//		be successfull
+	//
+	void Block4x4Encoding_ETC1::TryDegenerates3(void)
+	{
+
+		TryDifferential(m_boolMostLikelyFlip, 1, -2, -2);
+		TryDifferential(m_boolMostLikelyFlip, 1, -2, 2);
+		TryDifferential(m_boolMostLikelyFlip, 1, 2, -2);
+		TryDifferential(m_boolMostLikelyFlip, 1, 2, 2);
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// try version 4 of the degenerate search
+	// degenerate encodings use basecolor movement and a subset of the selectors to find useful encodings
+	// each subsequent version of the degenerate search uses more basecolor movement and is less likely to
+	//		be successfull
+	//
+	void Block4x4Encoding_ETC1::TryDegenerates4(void)
+	{
+
+		TryDifferential(m_boolMostLikelyFlip, 1, -4, 0);
+		TryDifferential(m_boolMostLikelyFlip, 1, 4, 0);
+		TryDifferential(m_boolMostLikelyFlip, 1, 0, 4);
+		TryDifferential(m_boolMostLikelyFlip, 1, 0, -4);
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// find the best selector for each pixel based on a particular basecolor and CW that have been previously set
+	// calculate the selectors for each half of the block separately
+	// set the block error as the sum of each half's error
+	//
+	void Block4x4Encoding_ETC1::CalculateSelectors()
+	{
+		if (m_boolFlip)
+		{
+			CalculateHalfOfTheSelectors(0, s_auiTopPixelMapping);
+			CalculateHalfOfTheSelectors(1, s_auiBottomPixelMapping);
+		}
+		else
+		{
+			CalculateHalfOfTheSelectors(0, s_auiLeftPixelMapping);
+			CalculateHalfOfTheSelectors(1, s_auiRightPixelMapping);
+		}
+
+		m_fError = m_fError1 + m_fError2;
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// choose best selectors for half of the block
+	// calculate the error for half of the block
+	//
+	void Block4x4Encoding_ETC1::CalculateHalfOfTheSelectors(unsigned int a_uiHalf,
+		const unsigned int *pauiPixelMapping)
+	{
+		static const bool DEBUG_PRINT = false;
+
+		ColorFloatRGBA *pfrgbaColor = a_uiHalf ? &m_frgbaColor2 : &m_frgbaColor1;
+		unsigned int *puiCW = a_uiHalf ? &m_uiCW2 : &m_uiCW1;
+
+		float *pfHalfError = a_uiHalf ? &m_fError2 : &m_fError1;
+		*pfHalfError = FLT_MAX;
+
+		// try each CW
+		for (unsigned int uiCW = 0; uiCW < CW_RANGES; uiCW++)
+		{
+			if (DEBUG_PRINT)
+			{
+				printf("\ncw=%u\n", uiCW);
+			}
+
+			unsigned int auiPixelSelectors[PIXELS / 2];
+			ColorFloatRGBA	afrgbaDecodedPixels[PIXELS / 2];
+			float afPixelErrors[PIXELS / 2] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX };
+
+			for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++)
+			{
+				if (DEBUG_PRINT)
+				{
+					printf("\tsource [%.2f,%.2f,%.2f]\n", m_pafrgbaSource[pauiPixelMapping[uiPixel]].fR,
+						m_pafrgbaSource[pauiPixelMapping[uiPixel]].fG, m_pafrgbaSource[pauiPixelMapping[uiPixel]].fB);
+				}
+
+				ColorFloatRGBA *pfrgbaSourcePixel = &m_pafrgbaSource[pauiPixelMapping[uiPixel]];
+				ColorFloatRGBA frgbaDecodedPixel;
+
+				for (unsigned int uiSelector = 0; uiSelector < SELECTORS; uiSelector++)
+				{
+					float fDeltaRGB = s_aafCwTable[uiCW][uiSelector];
+
+					frgbaDecodedPixel = (*pfrgbaColor + fDeltaRGB).ClampRGB();
+
+					float fPixelError;
+					
+					fPixelError = CalcPixelError(frgbaDecodedPixel, m_afDecodedAlphas[pauiPixelMapping[uiPixel]],
+														*pfrgbaSourcePixel);
+					
+					if (DEBUG_PRINT)
+					{
+						printf("\tpixel %u, index %u [%.2f,%.2f,%.2f], error %.2f", uiPixel, uiSelector,
+							frgbaDecodedPixel.fR,
+							frgbaDecodedPixel.fG,
+							frgbaDecodedPixel.fB,
+							fPixelError);
+					}
+
+					if (fPixelError < afPixelErrors[uiPixel])
+					{
+						if (DEBUG_PRINT)
+						{
+							printf(" *");
+						}
+
+						auiPixelSelectors[uiPixel] = uiSelector;
+						afrgbaDecodedPixels[uiPixel] = frgbaDecodedPixel;
+						afPixelErrors[uiPixel] = fPixelError;
+					}
+
+					if (DEBUG_PRINT)
+					{
+						printf("\n");
+					}
+				}
+			}
+
+			// add up all pixel errors
+			float fCWError = 0.0f;
+			for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++)
+			{
+				fCWError += afPixelErrors[uiPixel];
+			}
+			if (DEBUG_PRINT)
+			{
+				printf("\terror %.2f\n", fCWError);
+			}
+
+			// if best CW so far
+			if (fCWError < *pfHalfError)
+			{
+				*pfHalfError = fCWError;
+				*puiCW = uiCW;
+				for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++)
+				{
+					m_auiSelectors[pauiPixelMapping[uiPixel]] = auiPixelSelectors[uiPixel];
+					m_afrgbaDecodedColors[pauiPixelMapping[uiPixel]] = afrgbaDecodedPixels[uiPixel];
+				}
+			}
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// set the encoding bits based on encoding state
+	//
+	void Block4x4Encoding_ETC1::SetEncodingBits(void)
+	{
+		assert(m_mode == MODE_ETC1);
+
+		if (m_boolDiff)
+		{
+			int iRed1 = m_frgbaColor1.IntRed(31.0f);
+			int iGreen1 = m_frgbaColor1.IntGreen(31.0f);
+			int iBlue1 = m_frgbaColor1.IntBlue(31.0f);
+
+			int iRed2 = m_frgbaColor2.IntRed(31.0f);
+			int iGreen2 = m_frgbaColor2.IntGreen(31.0f);
+			int iBlue2 = m_frgbaColor2.IntBlue(31.0f);
+
+			int iDRed2 = iRed2 - iRed1;
+			int iDGreen2 = iGreen2 - iGreen1;
+			int iDBlue2 = iBlue2 - iBlue1;
+
+			assert(iDRed2 >= -4 && iDRed2 < 4);
+			assert(iDGreen2 >= -4 && iDGreen2 < 4);
+			assert(iDBlue2 >= -4 && iDBlue2 < 4);
+
+			m_pencodingbitsRGB8->differential.red1 = (unsigned int)iRed1;
+			m_pencodingbitsRGB8->differential.green1 = (unsigned int)iGreen1;
+			m_pencodingbitsRGB8->differential.blue1 = (unsigned int)iBlue1;
+
+			m_pencodingbitsRGB8->differential.dred2 = iDRed2;
+			m_pencodingbitsRGB8->differential.dgreen2 = iDGreen2;
+			m_pencodingbitsRGB8->differential.dblue2 = iDBlue2;
+		}
+		else
+		{
+			m_pencodingbitsRGB8->individual.red1 = (unsigned int)m_frgbaColor1.IntRed(15.0f);
+			m_pencodingbitsRGB8->individual.green1 = (unsigned int)m_frgbaColor1.IntGreen(15.0f);
+			m_pencodingbitsRGB8->individual.blue1 = (unsigned int)m_frgbaColor1.IntBlue(15.0f);
+
+			m_pencodingbitsRGB8->individual.red2 = (unsigned int)m_frgbaColor2.IntRed(15.0f);
+			m_pencodingbitsRGB8->individual.green2 = (unsigned int)m_frgbaColor2.IntGreen(15.0f);
+			m_pencodingbitsRGB8->individual.blue2 = (unsigned int)m_frgbaColor2.IntBlue(15.0f);
+		}
+
+		m_pencodingbitsRGB8->individual.cw1 = m_uiCW1;
+		m_pencodingbitsRGB8->individual.cw2 = m_uiCW2;
+
+		SetEncodingBits_Selectors();
+
+		m_pencodingbitsRGB8->individual.diff = (unsigned int)m_boolDiff;
+		m_pencodingbitsRGB8->individual.flip = (unsigned int)m_boolFlip;
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// set the selectors in the encoding bits
+	//
+	void Block4x4Encoding_ETC1::SetEncodingBits_Selectors(void)
+	{
+
+		m_pencodingbitsRGB8->individual.selectors = 0;
+		for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+		{
+			unsigned int uiSelector = m_auiSelectors[uiPixel];
+
+			// set index msb
+			m_pencodingbitsRGB8->individual.selectors |= (uiSelector >> 1) << (uiPixel ^ 8);
+
+			// set index lsb
+			m_pencodingbitsRGB8->individual.selectors |= (uiSelector & 1) << ((16 + uiPixel) ^ 8);
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// set the decoded colors and decoded alpha based on the encoding state
+	//
+	void Block4x4Encoding_ETC1::Decode(void)
+	{
+
+		const unsigned int *pauiPixelOrder = m_boolFlip ? s_auiPixelOrderFlip1 : s_auiPixelOrderFlip0;
+
+		for (unsigned int uiPixelOrder = 0; uiPixelOrder < PIXELS; uiPixelOrder++)
+		{
+			ColorFloatRGBA *pfrgbaCenter = uiPixelOrder < 8 ? &m_frgbaColor1 : &m_frgbaColor2;
+			unsigned int uiCW = uiPixelOrder < 8 ? m_uiCW1 : m_uiCW2;
+
+			unsigned int uiPixel = pauiPixelOrder[uiPixelOrder];
+
+			float fDelta = s_aafCwTable[uiCW][m_auiSelectors[uiPixel]];
+			m_afrgbaDecodedColors[uiPixel] = (*pfrgbaCenter + fDelta).ClampRGB();
+			m_afDecodedAlphas[uiPixel] = 1.0f;
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+
+} // namespace Etc

+ 186 - 0
thirdparty/etc2comp/EtcBlock4x4Encoding_ETC1.h

@@ -0,0 +1,186 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "EtcBlock4x4Encoding.h"
+#include "EtcBlock4x4EncodingBits.h"
+#include "EtcDifferentialTrys.h"
+#include "EtcIndividualTrys.h"
+
+namespace Etc
+{
+
+	// base class for Block4x4Encoding_RGB8
+	class Block4x4Encoding_ETC1 : public Block4x4Encoding
+	{
+	public:
+
+		Block4x4Encoding_ETC1(void);
+		virtual ~Block4x4Encoding_ETC1(void);
+
+		virtual void InitFromSource(Block4x4 *a_pblockParent,
+									ColorFloatRGBA *a_pafrgbaSource,
+
+									unsigned char *a_paucEncodingBits,
+									ErrorMetric a_errormetric);
+
+		virtual void InitFromEncodingBits(Block4x4 *a_pblockParent,
+											unsigned char *a_paucEncodingBits,
+											ColorFloatRGBA *a_pafrgbaSource, 
+
+											ErrorMetric a_errormetric);
+
+		virtual void PerformIteration(float a_fEffort);
+
+		inline virtual bool GetFlip(void)
+		{
+			return m_boolFlip;
+		}
+
+		inline virtual bool IsDifferential(void)
+		{
+			return m_boolDiff;
+		}
+
+		virtual void SetEncodingBits(void);
+
+		void Decode(void);
+
+		inline ColorFloatRGBA GetColor1(void) const
+		{
+			return m_frgbaColor1;
+		}
+
+		inline ColorFloatRGBA GetColor2(void) const
+		{
+			return m_frgbaColor2;
+		}
+
+		inline const unsigned int * GetSelectors(void) const
+		{
+			return m_auiSelectors;
+		}
+
+		inline unsigned int GetCW1(void) const
+		{
+			return m_uiCW1;
+		}
+
+		inline unsigned int GetCW2(void) const
+		{
+			return m_uiCW2;
+		}
+
+		inline bool HasSeverelyBentDifferentialColors(void) const
+		{
+			return m_boolSeverelyBentDifferentialColors;
+		}
+
+	protected:
+
+		static const unsigned int s_auiPixelOrderFlip0[PIXELS];
+		static const unsigned int s_auiPixelOrderFlip1[PIXELS];
+		static const unsigned int s_auiPixelOrderHScan[PIXELS];
+
+		static const unsigned int s_auiLeftPixelMapping[8];
+		static const unsigned int s_auiRightPixelMapping[8];
+		static const unsigned int s_auiTopPixelMapping[8];
+		static const unsigned int s_auiBottomPixelMapping[8];
+
+		static const unsigned int SELECTOR_BITS = 2;
+		static const unsigned int SELECTORS = 1 << SELECTOR_BITS;
+
+		static const unsigned int CW_BITS = 3;
+		static const unsigned int CW_RANGES = 1 << CW_BITS;
+
+		static float s_aafCwTable[CW_RANGES][SELECTORS];
+		static unsigned char s_aucDifferentialCwRange[256];
+
+		static const int MAX_DIFFERENTIAL = 3;
+		static const int MIN_DIFFERENTIAL = -4;
+
+		void InitFromEncodingBits_Selectors(void);
+
+		void PerformFirstIteration(void);
+		void CalculateMostLikelyFlip(void);
+
+		void TryDifferential(bool a_boolFlip, unsigned int a_uiRadius,
+								int a_iGrayOffset1, int a_iGrayOffset2);
+		void TryDifferentialHalf(DifferentialTrys::Half *a_phalf);
+
+		void TryIndividual(bool a_boolFlip, unsigned int a_uiRadius);
+		void TryIndividualHalf(IndividualTrys::Half *a_phalf);
+
+		void TryDegenerates1(void);
+		void TryDegenerates2(void);
+		void TryDegenerates3(void);
+		void TryDegenerates4(void);
+
+		void CalculateSelectors();
+		void CalculateHalfOfTheSelectors(unsigned int a_uiHalf,
+											const unsigned int *pauiPixelMapping);
+
+		// calculate the distance2 of r_frgbaPixel from r_frgbaTarget's gray line
+		inline float CalcGrayDistance2(ColorFloatRGBA &r_frgbaPixel, 
+										ColorFloatRGBA &r_frgbaTarget)
+		{
+			float fDeltaGray = ((r_frgbaPixel.fR - r_frgbaTarget.fR) +
+								(r_frgbaPixel.fG - r_frgbaTarget.fG) +
+								(r_frgbaPixel.fB - r_frgbaTarget.fB)) / 3.0f;
+
+			ColorFloatRGBA frgbaPointOnGrayLine = (r_frgbaTarget + fDeltaGray).ClampRGB();
+
+			float fDR = r_frgbaPixel.fR - frgbaPointOnGrayLine.fR;
+			float fDG = r_frgbaPixel.fG - frgbaPointOnGrayLine.fG;
+			float fDB = r_frgbaPixel.fB - frgbaPointOnGrayLine.fB;
+
+			return (fDR*fDR) + (fDG*fDG) + (fDB*fDB);
+		}
+
+		void SetEncodingBits_Selectors(void);
+
+		// intermediate encoding
+		bool			m_boolDiff;
+		bool			m_boolFlip;
+		ColorFloatRGBA	m_frgbaColor1;
+		ColorFloatRGBA	m_frgbaColor2;
+		unsigned int	m_uiCW1;
+		unsigned int	m_uiCW2;
+		unsigned int	m_auiSelectors[PIXELS];
+
+		// state shared between iterations
+		ColorFloatRGBA	m_frgbaSourceAverageLeft;
+		ColorFloatRGBA	m_frgbaSourceAverageRight;
+		ColorFloatRGBA	m_frgbaSourceAverageTop;
+		ColorFloatRGBA	m_frgbaSourceAverageBottom;
+		bool			m_boolMostLikelyFlip;
+
+		// stats
+		float			m_fError1;	// error for Etc1 half 1
+		float			m_fError2;	// error for Etc1 half 2
+		bool			m_boolSeverelyBentDifferentialColors;	// only valid if m_boolDiff;
+
+		// final encoding
+		Block4x4EncodingBits_RGB8 *m_pencodingbitsRGB8;		// or RGB8 portion of Block4x4EncodingBits_RGB8A8
+
+		private:
+
+		void CalculateSourceAverages(void);
+
+	};
+
+} // namespace Etc

+ 429 - 0
thirdparty/etc2comp/EtcBlock4x4Encoding_R11.cpp

@@ -0,0 +1,429 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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.
+ */
+
+/*
+EtcBlock4x4Encoding_R11.cpp
+
+Block4x4Encoding_R11 is the encoder to use when targetting file format R11 and SR11 (signed R11).  
+
+*/
+
+#include "EtcConfig.h"
+#include "EtcBlock4x4Encoding_R11.h"
+
+#include "EtcBlock4x4EncodingBits.h"
+#include "EtcBlock4x4.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <float.h>
+#include <limits>
+
+namespace Etc
+{
+
+	// modifier values to use for R11, SR11, RG11 and SRG11
+	float Block4x4Encoding_R11::s_aafModifierTable[MODIFIER_TABLE_ENTRYS][SELECTORS]
+	{
+		{ -3.0f / 255.0f, -6.0f / 255.0f,  -9.0f / 255.0f, -15.0f / 255.0f, 2.0f / 255.0f, 5.0f / 255.0f, 8.0f / 255.0f, 14.0f / 255.0f },
+		{ -3.0f / 255.0f, -7.0f / 255.0f, -10.0f / 255.0f, -13.0f / 255.0f, 2.0f / 255.0f, 6.0f / 255.0f, 9.0f / 255.0f, 12.0f / 255.0f },
+		{ -2.0f / 255.0f, -5.0f / 255.0f,  -8.0f / 255.0f, -13.0f / 255.0f, 1.0f / 255.0f, 4.0f / 255.0f, 7.0f / 255.0f, 12.0f / 255.0f },
+		{ -2.0f / 255.0f, -4.0f / 255.0f,  -6.0f / 255.0f, -13.0f / 255.0f, 1.0f / 255.0f, 3.0f / 255.0f, 5.0f / 255.0f, 12.0f / 255.0f },
+
+		{ -3.0f / 255.0f, -6.0f / 255.0f,  -8.0f / 255.0f, -12.0f / 255.0f, 2.0f / 255.0f, 5.0f / 255.0f, 7.0f / 255.0f, 11.0f / 255.0f },
+		{ -3.0f / 255.0f, -7.0f / 255.0f,  -9.0f / 255.0f, -11.0f / 255.0f, 2.0f / 255.0f, 6.0f / 255.0f, 8.0f / 255.0f, 10.0f / 255.0f },
+		{ -4.0f / 255.0f, -7.0f / 255.0f,  -8.0f / 255.0f, -11.0f / 255.0f, 3.0f / 255.0f, 6.0f / 255.0f, 7.0f / 255.0f, 10.0f / 255.0f },
+		{ -3.0f / 255.0f, -5.0f / 255.0f,  -8.0f / 255.0f, -11.0f / 255.0f, 2.0f / 255.0f, 4.0f / 255.0f, 7.0f / 255.0f, 10.0f / 255.0f },
+
+		{ -2.0f / 255.0f, -6.0f / 255.0f,  -8.0f / 255.0f, -10.0f / 255.0f, 1.0f / 255.0f, 5.0f / 255.0f, 7.0f / 255.0f,  9.0f / 255.0f },
+		{ -2.0f / 255.0f, -5.0f / 255.0f,  -8.0f / 255.0f, -10.0f / 255.0f, 1.0f / 255.0f, 4.0f / 255.0f, 7.0f / 255.0f,  9.0f / 255.0f },
+		{ -2.0f / 255.0f, -4.0f / 255.0f,  -8.0f / 255.0f, -10.0f / 255.0f, 1.0f / 255.0f, 3.0f / 255.0f, 7.0f / 255.0f,  9.0f / 255.0f },
+		{ -2.0f / 255.0f, -5.0f / 255.0f,  -7.0f / 255.0f, -10.0f / 255.0f, 1.0f / 255.0f, 4.0f / 255.0f, 6.0f / 255.0f,  9.0f / 255.0f },
+
+		{ -3.0f / 255.0f, -4.0f / 255.0f,  -7.0f / 255.0f, -10.0f / 255.0f, 2.0f / 255.0f, 3.0f / 255.0f, 6.0f / 255.0f,  9.0f / 255.0f },
+		{ -1.0f / 255.0f, -2.0f / 255.0f,  -3.0f / 255.0f, -10.0f / 255.0f, 0.0f / 255.0f, 1.0f / 255.0f, 2.0f / 255.0f,  9.0f / 255.0f },
+		{ -4.0f / 255.0f, -6.0f / 255.0f,  -8.0f / 255.0f,  -9.0f / 255.0f, 3.0f / 255.0f, 5.0f / 255.0f, 7.0f / 255.0f,  8.0f / 255.0f },
+		{ -3.0f / 255.0f, -5.0f / 255.0f,  -7.0f / 255.0f,  -9.0f / 255.0f, 2.0f / 255.0f, 4.0f / 255.0f, 6.0f / 255.0f,  8.0f / 255.0f }
+	};
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+	Block4x4Encoding_R11::Block4x4Encoding_R11(void)
+	{
+
+		m_pencodingbitsR11 = nullptr;
+
+	}
+
+	Block4x4Encoding_R11::~Block4x4Encoding_R11(void) {}
+	// ----------------------------------------------------------------------------------------------------
+	// initialization prior to encoding
+	// a_pblockParent points to the block associated with this encoding
+	// a_errormetric is used to choose the best encoding
+	// a_pafrgbaSource points to a 4x4 block subset of the source image
+	// a_paucEncodingBits points to the final encoding bits
+	//
+	void Block4x4Encoding_R11::InitFromSource(Block4x4 *a_pblockParent,
+		ColorFloatRGBA *a_pafrgbaSource,
+		unsigned char *a_paucEncodingBits, ErrorMetric a_errormetric)
+	{
+		Block4x4Encoding::Init(a_pblockParent, a_pafrgbaSource,a_errormetric);
+
+		m_pencodingbitsR11 = (Block4x4EncodingBits_R11 *)a_paucEncodingBits;
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// initialization from the encoding bits of a previous encoding
+	// a_pblockParent points to the block associated with this encoding
+	// a_errormetric is used to choose the best encoding
+	// a_pafrgbaSource points to a 4x4 block subset of the source image
+	// a_paucEncodingBits points to the final encoding bits of a previous encoding
+	//
+	void Block4x4Encoding_R11::InitFromEncodingBits(Block4x4 *a_pblockParent,
+		unsigned char *a_paucEncodingBits,
+		ColorFloatRGBA *a_pafrgbaSource,
+		ErrorMetric a_errormetric)
+	{
+		m_pencodingbitsR11 = (Block4x4EncodingBits_R11 *)a_paucEncodingBits;
+
+		// init RGB portion
+		Block4x4Encoding_RGB8::InitFromEncodingBits(a_pblockParent,
+			(unsigned char *)m_pencodingbitsR11,
+			a_pafrgbaSource,
+			a_errormetric);
+
+		// init R11 portion
+		{
+			m_mode = MODE_R11;
+			if (a_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_R11 || a_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_RG11)
+			{
+				m_fRedBase = (float)(signed char)m_pencodingbitsR11->data.base;
+			}
+			else
+			{
+				m_fRedBase = (float)(unsigned char)m_pencodingbitsR11->data.base;
+			}
+			m_fRedMultiplier = (float)m_pencodingbitsR11->data.multiplier;
+			m_uiRedModifierTableIndex = m_pencodingbitsR11->data.table;
+
+			unsigned long long int ulliSelectorBits = 0;
+			ulliSelectorBits |= (unsigned long long int)m_pencodingbitsR11->data.selectors0 << 40;
+			ulliSelectorBits |= (unsigned long long int)m_pencodingbitsR11->data.selectors1 << 32;
+			ulliSelectorBits |= (unsigned long long int)m_pencodingbitsR11->data.selectors2 << 24;
+			ulliSelectorBits |= (unsigned long long int)m_pencodingbitsR11->data.selectors3 << 16;
+			ulliSelectorBits |= (unsigned long long int)m_pencodingbitsR11->data.selectors4 << 8;
+			ulliSelectorBits |= (unsigned long long int)m_pencodingbitsR11->data.selectors5;
+			for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+			{
+				unsigned int uiShift = 45 - (3 * uiPixel);
+				m_auiRedSelectors[uiPixel] = (ulliSelectorBits >> uiShift) & (SELECTORS - 1);
+			}
+
+			// decode the red channel
+			// calc red error
+			for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+			{
+				float fDecodedPixelData = 0.0f;
+				if (a_pblockParent->GetImageSource()->GetFormat() == Image::Format::R11 || a_pblockParent->GetImageSource()->GetFormat() == Image::Format::RG11)
+				{
+					fDecodedPixelData = DecodePixelRed(m_fRedBase, m_fRedMultiplier,
+						m_uiRedModifierTableIndex,
+						m_auiRedSelectors[uiPixel]);
+				}
+				else if (a_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_R11 || a_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_RG11)
+				{
+					fDecodedPixelData = DecodePixelRed(m_fRedBase + 128, m_fRedMultiplier,
+						m_uiRedModifierTableIndex,
+						m_auiRedSelectors[uiPixel]);
+				}
+				else
+				{
+					assert(0);
+				}
+				m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA(fDecodedPixelData, 0.0f, 0.0f, 1.0f);
+			}
+			CalcBlockError();
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// perform a single encoding iteration
+	// replace the encoding if a better encoding was found
+	// subsequent iterations generally take longer for each iteration
+	// set m_boolDone if encoding is perfect or encoding is finished based on a_fEffort
+	//
+	void Block4x4Encoding_R11::PerformIteration(float a_fEffort)
+	{
+		assert(!m_boolDone);
+		m_mode = MODE_R11;
+
+		switch (m_uiEncodingIterations)
+		{
+		case 0:
+			m_fError = FLT_MAX;
+			m_fRedBlockError = FLT_MAX;		// artificially high value
+			CalculateR11(8, 0.0f, 0.0f);
+			m_fError = m_fRedBlockError;
+			break;
+
+		case 1:
+			CalculateR11(8, 2.0f, 1.0f);
+			m_fError = m_fRedBlockError;
+			if (a_fEffort <= 24.5f)
+			{
+				m_boolDone = true;
+			}
+			break;
+
+		case 2:
+			CalculateR11(8, 12.0f, 1.0f);
+			m_fError = m_fRedBlockError;
+			if (a_fEffort <= 49.5f)
+			{
+				m_boolDone = true;
+			}
+			break;
+
+		case 3:
+			CalculateR11(7, 6.0f, 1.0f);
+			m_fError = m_fRedBlockError;
+			break;
+
+		case 4:
+			CalculateR11(6, 3.0f, 1.0f);
+			m_fError = m_fRedBlockError;
+			break;
+
+		case 5:
+			CalculateR11(5, 1.0f, 0.0f);
+			m_fError = m_fRedBlockError;
+			m_boolDone = true;
+			break;
+
+		default:
+			assert(0);
+			break;
+		}
+
+		m_uiEncodingIterations++;
+		SetDoneIfPerfect();
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// find the best combination of base color, multiplier and selectors
+	//
+	// a_uiSelectorsUsed limits the number of selector combinations to try
+	// a_fBaseRadius limits the range of base colors to try
+	// a_fMultiplierRadius limits the range of multipliers to try
+	//
+	void Block4x4Encoding_R11::CalculateR11(unsigned int a_uiSelectorsUsed, 
+												float a_fBaseRadius, float a_fMultiplierRadius)
+	{
+		// maps from virtual (monotonic) selector to ETC selector
+		static const unsigned int auiVirtualSelectorMap[8] = {3, 2, 1, 0, 4, 5, 6, 7};
+
+		// find min/max red
+		float fMinRed = 1.0f;
+		float fMaxRed = 0.0f;
+		for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+		{
+			// ignore border pixels
+			float fAlpha = m_pafrgbaSource[uiPixel].fA;
+			if (isnan(fAlpha))
+			{
+				continue;
+			}
+
+			float fRed = m_pafrgbaSource[uiPixel].fR;
+
+			if (fRed < fMinRed)
+			{
+				fMinRed = fRed;
+			}
+			if (fRed > fMaxRed)
+			{
+				fMaxRed = fRed;
+			}
+		}
+		assert(fMinRed <= fMaxRed);
+
+		float fRedRange = (fMaxRed - fMinRed);
+
+		// try each modifier table entry							  
+		for (unsigned int uiTableEntry = 0; uiTableEntry < MODIFIER_TABLE_ENTRYS; uiTableEntry++)
+		{
+			for (unsigned int uiMinVirtualSelector = 0; 
+					uiMinVirtualSelector <= (8- a_uiSelectorsUsed); 
+					uiMinVirtualSelector++)
+			{
+				unsigned int uiMaxVirtualSelector = uiMinVirtualSelector + a_uiSelectorsUsed - 1;
+
+				unsigned int uiMinSelector = auiVirtualSelectorMap[uiMinVirtualSelector];
+				unsigned int uiMaxSelector = auiVirtualSelectorMap[uiMaxVirtualSelector];
+
+				float fTableEntryCenter = -s_aafModifierTable[uiTableEntry][uiMinSelector];
+
+				float fTableEntryRange = s_aafModifierTable[uiTableEntry][uiMaxSelector] -
+											s_aafModifierTable[uiTableEntry][uiMinSelector];
+
+				float fCenterRatio = fTableEntryCenter / fTableEntryRange;
+
+				float fCenter = fMinRed + fCenterRatio*fRedRange;
+				fCenter = roundf(255.0f * fCenter) / 255.0f;
+
+				float fMinBase = fCenter - (a_fBaseRadius / 255.0f);
+				if (fMinBase < 0.0f)
+				{
+					fMinBase = 0.0f;
+				}
+
+				float fMaxBase = fCenter + (a_fBaseRadius / 255.0f);
+				if (fMaxBase > 1.0f)
+				{
+					fMaxBase = 1.0f;
+				}
+
+				for (float fBase = fMinBase; fBase <= fMaxBase; fBase += (0.999999f / 255.0f))
+				{
+					float fRangeMultiplier = roundf(fRedRange / fTableEntryRange);
+
+					float fMinMultiplier = fRangeMultiplier - a_fMultiplierRadius;
+					if (fMinMultiplier < 1.0f)
+					{
+						fMinMultiplier = 0.0f;
+					}
+					else if (fMinMultiplier > 15.0f)
+					{
+						fMinMultiplier = 15.0f;
+					}
+
+					float fMaxMultiplier = fRangeMultiplier + a_fMultiplierRadius;
+					if (fMaxMultiplier < 1.0f)
+					{
+						fMaxMultiplier = 1.0f;
+					}
+					else if (fMaxMultiplier > 15.0f)
+					{
+						fMaxMultiplier = 15.0f;
+					}
+
+					for (float fMultiplier = fMinMultiplier; fMultiplier <= fMaxMultiplier; fMultiplier += 1.0f)
+					{
+						// find best selector for each pixel
+						unsigned int auiBestSelectors[PIXELS];
+						float afBestRedError[PIXELS];
+						float afBestPixelRed[PIXELS];
+
+						for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+						{
+							float fBestPixelRedError = FLT_MAX;
+
+							for (unsigned int uiSelector = 0; uiSelector < SELECTORS; uiSelector++)
+							{
+								float fPixelRed = DecodePixelRed(fBase * 255.0f, fMultiplier, uiTableEntry, uiSelector);
+
+								ColorFloatRGBA frgba(fPixelRed, m_pafrgbaSource[uiPixel].fG,0.0f,1.0f);
+
+								float fPixelRedError = CalcPixelError(frgba, 1.0f, m_pafrgbaSource[uiPixel]);
+
+								if (fPixelRedError < fBestPixelRedError)
+								{
+									fBestPixelRedError = fPixelRedError;
+									auiBestSelectors[uiPixel] = uiSelector;
+									afBestRedError[uiPixel] = fBestPixelRedError;
+									afBestPixelRed[uiPixel] = fPixelRed;
+								}
+							}
+						}
+						float fBlockError = 0.0f;  
+						for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+						{
+							fBlockError += afBestRedError[uiPixel];
+						}
+						if (fBlockError < m_fRedBlockError)
+						{
+							m_fRedBlockError = fBlockError;
+
+							if (m_pblockParent->GetImageSource()->GetFormat() == Image::Format::R11 || m_pblockParent->GetImageSource()->GetFormat() == Image::Format::RG11)
+							{
+								m_fRedBase = 255.0f * fBase;
+							}
+							else if (m_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_R11 || m_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_RG11)
+							{
+								m_fRedBase = (fBase * 255) - 128;
+							}
+							else
+							{
+								assert(0);
+							}
+							m_fRedMultiplier = fMultiplier;
+							m_uiRedModifierTableIndex = uiTableEntry;
+
+							for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+							{
+								m_auiRedSelectors[uiPixel] = auiBestSelectors[uiPixel];
+								float fBestPixelRed = afBestPixelRed[uiPixel];
+								m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA(fBestPixelRed, 0.0f, 0.0f, 1.0f);
+								m_afDecodedAlphas[uiPixel] = 1.0f;
+							}
+						}
+					}
+				}
+
+			}
+		}
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// set the encoding bits based on encoding state
+	//
+	void Block4x4Encoding_R11::SetEncodingBits(void)
+	{
+		if (m_pblockParent->GetImageSource()->GetFormat() == Image::Format::R11 || m_pblockParent->GetImageSource()->GetFormat() == Image::Format::RG11)
+		{
+			m_pencodingbitsR11->data.base = (unsigned char)roundf(m_fRedBase);
+		}
+		else if (m_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_R11 || m_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_RG11)
+		{
+			m_pencodingbitsR11->data.base = (signed char)roundf(m_fRedBase);
+		}
+		else
+		{
+			assert(0);
+		}
+		m_pencodingbitsR11->data.table = m_uiRedModifierTableIndex;
+		m_pencodingbitsR11->data.multiplier = (unsigned char)roundf(m_fRedMultiplier);
+
+		unsigned long long int ulliSelectorBits = 0;
+		for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+		{
+			unsigned int uiShift = 45 - (3 * uiPixel);
+			ulliSelectorBits |= ((unsigned long long int)m_auiRedSelectors[uiPixel]) << uiShift;
+		}
+
+		m_pencodingbitsR11->data.selectors0 = ulliSelectorBits >> 40;
+		m_pencodingbitsR11->data.selectors1 = ulliSelectorBits >> 32;
+		m_pencodingbitsR11->data.selectors2 = ulliSelectorBits >> 24;
+		m_pencodingbitsR11->data.selectors3 = ulliSelectorBits >> 16;
+		m_pencodingbitsR11->data.selectors4 = ulliSelectorBits >> 8;
+		m_pencodingbitsR11->data.selectors5 = ulliSelectorBits;
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+}

+ 122 - 0
thirdparty/etc2comp/EtcBlock4x4Encoding_R11.h

@@ -0,0 +1,122 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "EtcBlock4x4Encoding_RGB8.h"
+
+namespace Etc
+{
+	class Block4x4EncodingBits_R11;
+
+	// ################################################################################
+	// Block4x4Encoding_R11
+	// ################################################################################
+
+	class Block4x4Encoding_R11 : public Block4x4Encoding_RGB8
+	{
+	public:
+
+		Block4x4Encoding_R11(void);
+		virtual ~Block4x4Encoding_R11(void);
+
+		virtual void InitFromSource(Block4x4 *a_pblockParent,
+			ColorFloatRGBA *a_pafrgbaSource,
+			unsigned char *a_paucEncodingBits, ErrorMetric a_errormetric);
+
+		virtual void InitFromEncodingBits(Block4x4 *a_pblockParent,
+			unsigned char *a_paucEncodingBits,
+			ColorFloatRGBA *a_pafrgbaSource,
+			ErrorMetric a_errormetric);
+
+		virtual void PerformIteration(float a_fEffort);
+
+		virtual void SetEncodingBits(void);
+
+		inline float GetRedBase(void) const
+		{
+			return m_fRedBase;
+		}
+
+		inline float GetRedMultiplier(void) const
+		{
+			return m_fRedMultiplier;
+		}
+
+		inline int GetRedTableIndex(void) const
+		{
+			return m_uiRedModifierTableIndex;
+		}
+
+		inline const unsigned int * GetRedSelectors(void) const
+		{
+			return m_auiRedSelectors;
+		}
+
+	protected:
+
+		static const unsigned int MODIFIER_TABLE_ENTRYS = 16;
+		static const unsigned int SELECTOR_BITS = 3;
+		static const unsigned int SELECTORS = 1 << SELECTOR_BITS;
+
+		static float s_aafModifierTable[MODIFIER_TABLE_ENTRYS][SELECTORS];
+
+		void CalculateR11(unsigned int a_uiSelectorsUsed, 
+							float a_fBaseRadius, float a_fMultiplierRadius);
+
+		
+
+	
+		inline float DecodePixelRed(float a_fBase, float a_fMultiplier,
+			unsigned int a_uiTableIndex, unsigned int a_uiSelector)
+		{
+			float fMultiplier = a_fMultiplier;
+			if (fMultiplier <= 0.0f)
+			{
+				fMultiplier = 1.0f / 8.0f;
+			}
+
+			float fPixelRed = a_fBase * 8 + 4 +
+				8 * fMultiplier*s_aafModifierTable[a_uiTableIndex][a_uiSelector]*255;
+			fPixelRed /= 2047.0f;
+
+			if (fPixelRed < 0.0f)
+			{
+				fPixelRed = 0.0f;
+			}
+			else if (fPixelRed > 1.0f)
+			{
+				fPixelRed = 1.0f;
+			}
+
+			return fPixelRed;
+		}
+
+		Block4x4EncodingBits_R11 *m_pencodingbitsR11;
+
+		float m_fRedBase;
+		float m_fRedMultiplier;
+		float m_fRedBlockError;
+		unsigned int m_uiRedModifierTableIndex;
+		unsigned int m_auiRedSelectors[PIXELS];
+
+		
+	};
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+
+} // namespace Etc

+ 447 - 0
thirdparty/etc2comp/EtcBlock4x4Encoding_RG11.cpp

@@ -0,0 +1,447 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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.
+ */
+
+/*
+EtcBlock4x4Encoding_RG11.cpp
+
+Block4x4Encoding_RG11 is the encoder to use when targetting file format RG11 and SRG11 (signed RG11).
+
+*/
+
+#include "EtcConfig.h"
+#include "EtcBlock4x4Encoding_RG11.h"
+
+#include "EtcBlock4x4EncodingBits.h"
+#include "EtcBlock4x4.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <float.h>
+#include <limits>
+
+namespace Etc
+{
+	// ----------------------------------------------------------------------------------------------------
+	//
+	Block4x4Encoding_RG11::Block4x4Encoding_RG11(void)
+	{
+		m_pencodingbitsRG11 = nullptr;
+	}
+
+	Block4x4Encoding_RG11::~Block4x4Encoding_RG11(void) {}
+	// ----------------------------------------------------------------------------------------------------
+	// initialization prior to encoding
+	// a_pblockParent points to the block associated with this encoding
+	// a_errormetric is used to choose the best encoding
+	// a_pafrgbaSource points to a 4x4 block subset of the source image
+	// a_paucEncodingBits points to the final encoding bits
+	//
+	void Block4x4Encoding_RG11::InitFromSource(Block4x4 *a_pblockParent,
+		ColorFloatRGBA *a_pafrgbaSource,
+		unsigned char *a_paucEncodingBits, ErrorMetric a_errormetric)
+	{
+		Block4x4Encoding::Init(a_pblockParent, a_pafrgbaSource,a_errormetric);
+
+		m_pencodingbitsRG11 = (Block4x4EncodingBits_RG11 *)a_paucEncodingBits;
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// initialization from the encoding bits of a previous encoding
+	// a_pblockParent points to the block associated with this encoding
+	// a_errormetric is used to choose the best encoding
+	// a_pafrgbaSource points to a 4x4 block subset of the source image
+	// a_paucEncodingBits points to the final encoding bits of a previous encoding
+	//
+	void Block4x4Encoding_RG11::InitFromEncodingBits(Block4x4 *a_pblockParent,
+		unsigned char *a_paucEncodingBits,
+		ColorFloatRGBA *a_pafrgbaSource,
+		ErrorMetric a_errormetric)
+	{
+
+		m_pencodingbitsRG11 = (Block4x4EncodingBits_RG11 *)a_paucEncodingBits;
+
+		// init RGB portion
+		Block4x4Encoding_RGB8::InitFromEncodingBits(a_pblockParent,
+			(unsigned char *)m_pencodingbitsRG11,
+			a_pafrgbaSource,
+			a_errormetric);
+		m_fError = 0.0f;
+
+		{
+			m_mode = MODE_RG11;
+			if (a_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_RG11)
+			{
+				m_fRedBase = (float)(signed char)m_pencodingbitsRG11->data.baseR;
+				m_fGrnBase = (float)(signed char)m_pencodingbitsRG11->data.baseG;
+			}
+			else
+			{
+				m_fRedBase = (float)(unsigned char)m_pencodingbitsRG11->data.baseR;
+				m_fGrnBase = (float)(unsigned char)m_pencodingbitsRG11->data.baseG;
+			}
+			m_fRedMultiplier = (float)m_pencodingbitsRG11->data.multiplierR;
+			m_fGrnMultiplier = (float)m_pencodingbitsRG11->data.multiplierG;
+			m_uiRedModifierTableIndex = m_pencodingbitsRG11->data.tableIndexR;
+			m_uiGrnModifierTableIndex = m_pencodingbitsRG11->data.tableIndexG;
+
+			unsigned long long int ulliSelectorBitsR = 0;
+			ulliSelectorBitsR |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsR0 << 40;
+			ulliSelectorBitsR |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsR1 << 32;
+			ulliSelectorBitsR |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsR2 << 24;
+			ulliSelectorBitsR |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsR3 << 16;
+			ulliSelectorBitsR |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsR4 << 8;
+			ulliSelectorBitsR |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsR5;
+
+			unsigned long long int ulliSelectorBitsG = 0;
+			ulliSelectorBitsG |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsG0 << 40;
+			ulliSelectorBitsG |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsG1 << 32;
+			ulliSelectorBitsG |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsG2 << 24;
+			ulliSelectorBitsG |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsG3 << 16;
+			ulliSelectorBitsG |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsG4 << 8;
+			ulliSelectorBitsG |= (unsigned long long int)m_pencodingbitsRG11->data.selectorsG5;
+
+			
+			for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+			{
+				unsigned int uiShift = 45 - (3 * uiPixel);
+				m_auiRedSelectors[uiPixel] = (ulliSelectorBitsR >> uiShift) & (SELECTORS - 1);
+				m_auiGrnSelectors[uiPixel] = (ulliSelectorBitsG >> uiShift) & (SELECTORS - 1);
+			}
+
+			
+			for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+			{
+				float fRedDecodedData = 0.0f;
+				float fGrnDecodedData = 0.0f;
+				if (a_pblockParent->GetImageSource()->GetFormat() == Image::Format::RG11)
+				{
+					fRedDecodedData = DecodePixelRed(m_fRedBase, m_fRedMultiplier, m_uiRedModifierTableIndex, m_auiRedSelectors[uiPixel]);
+					fGrnDecodedData = DecodePixelRed(m_fGrnBase, m_fGrnMultiplier, m_uiGrnModifierTableIndex, m_auiGrnSelectors[uiPixel]);
+				}
+				else if (a_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_RG11)
+				{
+					fRedDecodedData = DecodePixelRed(m_fRedBase + 128, m_fRedMultiplier, m_uiRedModifierTableIndex, m_auiRedSelectors[uiPixel]);
+					fGrnDecodedData = DecodePixelRed(m_fGrnBase + 128, m_fGrnMultiplier, m_uiGrnModifierTableIndex, m_auiGrnSelectors[uiPixel]);
+				}
+				else
+				{
+					assert(0);
+				}
+				m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA(fRedDecodedData, fGrnDecodedData, 0.0f, 1.0f);
+			}
+
+		}
+
+		CalcBlockError();
+ 	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// perform a single encoding iteration
+	// replace the encoding if a better encoding was found
+	// subsequent iterations generally take longer for each iteration
+	// set m_boolDone if encoding is perfect or encoding is finished based on a_fEffort
+	//
+	void Block4x4Encoding_RG11::PerformIteration(float a_fEffort)
+	{
+		assert(!m_boolDone);
+
+		switch (m_uiEncodingIterations)
+		{
+		case 0:
+			m_fError = FLT_MAX;
+			m_fGrnBlockError = FLT_MAX;		// artificially high value
+			m_fRedBlockError = FLT_MAX;
+			CalculateR11(8, 0.0f, 0.0f);
+			CalculateG11(8, 0.0f, 0.0f);
+			m_fError = (m_fGrnBlockError + m_fRedBlockError);
+			break;
+
+		case 1:
+			CalculateR11(8, 2.0f, 1.0f);
+			CalculateG11(8, 2.0f, 1.0f);
+			m_fError = (m_fGrnBlockError + m_fRedBlockError);
+			if (a_fEffort <= 24.5f)
+			{
+				m_boolDone = true;
+			}
+			break;
+
+		case 2:
+			CalculateR11(8, 12.0f, 1.0f);
+			CalculateG11(8, 12.0f, 1.0f);
+			m_fError = (m_fGrnBlockError + m_fRedBlockError);
+			if (a_fEffort <= 49.5f)
+			{
+				m_boolDone = true;
+			}
+			break;
+
+		case 3:
+			CalculateR11(7, 6.0f, 1.0f);
+			CalculateG11(7, 6.0f, 1.0f);
+			m_fError = (m_fGrnBlockError + m_fRedBlockError);
+			break;
+
+		case 4:
+			CalculateR11(6, 3.0f, 1.0f);
+			CalculateG11(6, 3.0f, 1.0f);
+			m_fError = (m_fGrnBlockError + m_fRedBlockError);
+			break;
+
+		case 5:
+			CalculateR11(5, 1.0f, 0.0f);
+			CalculateG11(5, 1.0f, 0.0f);
+			m_fError = (m_fGrnBlockError + m_fRedBlockError);
+			m_boolDone = true;
+			break;
+
+		default:
+			assert(0);
+			break;
+		}
+
+		m_uiEncodingIterations++;
+		SetDoneIfPerfect();
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// find the best combination of base color, multiplier and selectors
+	//
+	// a_uiSelectorsUsed limits the number of selector combinations to try
+	// a_fBaseRadius limits the range of base colors to try
+	// a_fMultiplierRadius limits the range of multipliers to try
+	//
+	void Block4x4Encoding_RG11::CalculateG11(unsigned int a_uiSelectorsUsed,
+		float a_fBaseRadius, float a_fMultiplierRadius)
+	{
+		// maps from virtual (monotonic) selector to etc selector
+		static const unsigned int auiVirtualSelectorMap[8] = { 3, 2, 1, 0, 4, 5, 6, 7 };
+
+		// find min/max Grn
+		float fMinGrn = 1.0f;
+		float fMaxGrn = 0.0f;
+		for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+		{
+			// ignore border pixels
+			float fAlpha = m_pafrgbaSource[uiPixel].fA;
+			if (isnan(fAlpha))
+			{
+				continue;
+			}
+
+			float fGrn = m_pafrgbaSource[uiPixel].fG;
+
+			if (fGrn < fMinGrn)
+			{
+				fMinGrn = fGrn;
+			}
+			if (fGrn > fMaxGrn)
+			{
+				fMaxGrn = fGrn;
+			}
+		}
+		assert(fMinGrn <= fMaxGrn);
+
+		float fGrnRange = (fMaxGrn - fMinGrn);
+
+		// try each modifier table entry							  
+		for (unsigned int uiTableEntry = 0; uiTableEntry < MODIFIER_TABLE_ENTRYS; uiTableEntry++)
+		{
+			for (unsigned int uiMinVirtualSelector = 0;
+			uiMinVirtualSelector <= (8 - a_uiSelectorsUsed);
+				uiMinVirtualSelector++)
+			{
+				unsigned int uiMaxVirtualSelector = uiMinVirtualSelector + a_uiSelectorsUsed - 1;
+
+				unsigned int uiMinSelector = auiVirtualSelectorMap[uiMinVirtualSelector];
+				unsigned int uiMaxSelector = auiVirtualSelectorMap[uiMaxVirtualSelector];
+
+				float fTableEntryCenter = -s_aafModifierTable[uiTableEntry][uiMinSelector];
+
+				float fTableEntryRange = s_aafModifierTable[uiTableEntry][uiMaxSelector] -
+					s_aafModifierTable[uiTableEntry][uiMinSelector];
+
+				float fCenterRatio = fTableEntryCenter / fTableEntryRange;
+
+				float fCenter = fMinGrn + fCenterRatio*fGrnRange;
+				fCenter = roundf(255.0f * fCenter) / 255.0f;
+
+				float fMinBase = fCenter - (a_fBaseRadius / 255.0f);
+				if (fMinBase < 0.0f)
+				{
+					fMinBase = 0.0f;
+				}
+
+				float fMaxBase = fCenter + (a_fBaseRadius / 255.0f);
+				if (fMaxBase > 1.0f)
+				{
+					fMaxBase = 1.0f;
+				}
+
+				for (float fBase = fMinBase; fBase <= fMaxBase; fBase += (0.999999f / 255.0f))
+				{
+					float fRangeMultiplier = roundf(fGrnRange / fTableEntryRange);
+
+					float fMinMultiplier = fRangeMultiplier - a_fMultiplierRadius;
+					if (fMinMultiplier < 1.0f)
+					{
+						fMinMultiplier = 0.0f;
+					}
+					else if (fMinMultiplier > 15.0f)
+					{
+						fMinMultiplier = 15.0f;
+					}
+
+					float fMaxMultiplier = fRangeMultiplier + a_fMultiplierRadius;
+					if (fMaxMultiplier < 1.0f)
+					{
+						fMaxMultiplier = 1.0f;
+					}
+					else if (fMaxMultiplier > 15.0f)
+					{
+						fMaxMultiplier = 15.0f;
+					}
+
+					for (float fMultiplier = fMinMultiplier; fMultiplier <= fMaxMultiplier; fMultiplier += 1.0f)
+					{
+						// find best selector for each pixel
+						unsigned int auiBestSelectors[PIXELS];
+						float afBestGrnError[PIXELS];
+						float afBestPixelGrn[PIXELS];
+
+						for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+						{
+							float fBestPixelGrnError = FLT_MAX;
+
+							for (unsigned int uiSelector = 0; uiSelector < SELECTORS; uiSelector++)
+							{
+								//DecodePixelRed is not red channel specific
+								float fPixelGrn = DecodePixelRed(fBase * 255.0f, fMultiplier, uiTableEntry, uiSelector);
+								
+								ColorFloatRGBA frgba(m_pafrgbaSource[uiPixel].fR, fPixelGrn, 0.0f, 1.0f);
+									
+								float fPixelGrnError = CalcPixelError(frgba, 1.0f, m_pafrgbaSource[uiPixel]);
+
+								if (fPixelGrnError < fBestPixelGrnError)
+								{
+									fBestPixelGrnError = fPixelGrnError;
+									auiBestSelectors[uiPixel] = uiSelector;
+									afBestGrnError[uiPixel] = fBestPixelGrnError;
+									afBestPixelGrn[uiPixel] = fPixelGrn;
+								}
+							}
+						}
+						float fBlockError = 0.0f;
+						for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+						{
+							fBlockError += afBestGrnError[uiPixel];
+						}
+
+						if (fBlockError < m_fGrnBlockError)
+						{
+							m_fGrnBlockError = fBlockError;
+
+							if (m_pblockParent->GetImageSource()->GetFormat() == Image::Format::RG11)
+							{
+								m_fGrnBase = 255.0f * fBase;
+							}
+							else if (m_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_RG11)
+							{
+								m_fGrnBase = (fBase * 255) - 128;
+							}
+							else
+							{
+								assert(0);
+							}
+							m_fGrnMultiplier = fMultiplier;
+							m_uiGrnModifierTableIndex = uiTableEntry;
+							for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+							{
+								m_auiGrnSelectors[uiPixel] = auiBestSelectors[uiPixel];
+								m_afrgbaDecodedColors[uiPixel].fG = afBestPixelGrn[uiPixel];
+								m_afDecodedAlphas[uiPixel] = 1.0f;
+							}
+						}
+					}
+				}
+
+			}
+		}
+	}
+	
+	// ----------------------------------------------------------------------------------------------------
+	// set the encoding bits based on encoding state
+	//
+	void Block4x4Encoding_RG11::SetEncodingBits(void)
+	{
+		unsigned long long int ulliSelectorBitsR = 0;
+		unsigned long long int ulliSelectorBitsG = 0;
+		for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+		{
+			unsigned int uiShift = 45 - (3 * uiPixel);
+			ulliSelectorBitsR |= ((unsigned long long int)m_auiRedSelectors[uiPixel]) << uiShift;
+			ulliSelectorBitsG |= ((unsigned long long int)m_auiGrnSelectors[uiPixel]) << uiShift;
+		}
+		if (m_pblockParent->GetImageSource()->GetFormat() == Image::Format::RG11)
+		{
+			m_pencodingbitsRG11->data.baseR = (unsigned char)roundf(m_fRedBase);
+		}
+		else if (m_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_RG11)
+		{
+			m_pencodingbitsRG11->data.baseR = (signed char)roundf(m_fRedBase);
+		}
+		else
+		{
+			assert(0);
+		}
+		m_pencodingbitsRG11->data.tableIndexR = m_uiRedModifierTableIndex;
+		m_pencodingbitsRG11->data.multiplierR = (unsigned char)roundf(m_fRedMultiplier);
+
+		m_pencodingbitsRG11->data.selectorsR0 = ulliSelectorBitsR >> 40;
+		m_pencodingbitsRG11->data.selectorsR1 = ulliSelectorBitsR >> 32;
+		m_pencodingbitsRG11->data.selectorsR2 = ulliSelectorBitsR >> 24;
+		m_pencodingbitsRG11->data.selectorsR3 = ulliSelectorBitsR >> 16;
+		m_pencodingbitsRG11->data.selectorsR4 = ulliSelectorBitsR >> 8;
+		m_pencodingbitsRG11->data.selectorsR5 = ulliSelectorBitsR;
+
+		if (m_pblockParent->GetImageSource()->GetFormat() == Image::Format::RG11)
+		{
+			m_pencodingbitsRG11->data.baseG = (unsigned char)roundf(m_fGrnBase);
+		}
+		else if (m_pblockParent->GetImageSource()->GetFormat() == Image::Format::SIGNED_RG11)
+		{
+			m_pencodingbitsRG11->data.baseG = (signed char)roundf(m_fGrnBase);
+		}
+		else
+		{
+			assert(0);
+		}
+		m_pencodingbitsRG11->data.tableIndexG = m_uiGrnModifierTableIndex;
+		m_pencodingbitsRG11->data.multiplierG = (unsigned char)roundf(m_fGrnMultiplier);
+
+		m_pencodingbitsRG11->data.selectorsG0 = ulliSelectorBitsG >> 40;
+		m_pencodingbitsRG11->data.selectorsG1 = ulliSelectorBitsG >> 32;
+		m_pencodingbitsRG11->data.selectorsG2 = ulliSelectorBitsG >> 24;
+		m_pencodingbitsRG11->data.selectorsG3 = ulliSelectorBitsG >> 16;
+		m_pencodingbitsRG11->data.selectorsG4 = ulliSelectorBitsG >> 8;
+		m_pencodingbitsRG11->data.selectorsG5 = ulliSelectorBitsG;
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+}

+ 86 - 0
thirdparty/etc2comp/EtcBlock4x4Encoding_RG11.h

@@ -0,0 +1,86 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "EtcBlock4x4Encoding_RGB8.h"
+#include "EtcBlock4x4Encoding_R11.h"
+
+namespace Etc
+{
+	class Block4x4EncodingBits_RG11;
+
+	// ################################################################################
+	// Block4x4Encoding_RG11
+	// ################################################################################
+
+	class Block4x4Encoding_RG11 : public Block4x4Encoding_R11
+	{
+		float m_fGrnBase;
+		float m_fGrnMultiplier;
+		float m_fGrnBlockError;
+		unsigned int m_auiGrnSelectors[PIXELS];
+		unsigned int m_uiGrnModifierTableIndex;
+	public:
+
+		Block4x4Encoding_RG11(void);
+		virtual ~Block4x4Encoding_RG11(void);
+
+		virtual void InitFromSource(Block4x4 *a_pblockParent,
+			ColorFloatRGBA *a_pafrgbaSource,
+
+			unsigned char *a_paucEncodingBits, ErrorMetric a_errormetric);
+
+		virtual void InitFromEncodingBits(Block4x4 *a_pblockParent,
+			unsigned char *a_paucEncodingBits,
+			ColorFloatRGBA *a_pafrgbaSource,
+
+			ErrorMetric a_errormetric);
+
+		virtual void PerformIteration(float a_fEffort);
+
+		virtual void SetEncodingBits(void);
+
+		Block4x4EncodingBits_RG11 *m_pencodingbitsRG11;
+
+		void CalculateG11(unsigned int a_uiSelectorsUsed, float a_fBaseRadius, float a_fMultiplierRadius);
+
+		inline float GetGrnBase(void) const
+		{
+			return m_fGrnBase;
+		}
+
+		inline float GetGrnMultiplier(void) const
+		{
+			return m_fGrnMultiplier;
+		}
+
+		inline int GetGrnTableIndex(void) const
+		{
+			return m_uiGrnModifierTableIndex;
+		}
+
+		inline const unsigned int * GetGrnSelectors(void) const
+		{
+			return m_auiGrnSelectors;
+		}
+
+	};
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+
+} // namespace Etc

+ 1730 - 0
thirdparty/etc2comp/EtcBlock4x4Encoding_RGB8.cpp

@@ -0,0 +1,1730 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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.
+ */
+
+/*
+EtcBlock4x4Encoding_RGB8.cpp
+
+Block4x4Encoding_RGB8 is the encoder to use for the ETC2 extensions when targetting file format RGB8.  
+This encoder is also used for the ETC2 subset of file format RGBA8.
+
+Block4x4Encoding_ETC1 encodes the ETC1 subset of RGB8.
+
+*/
+
+#include "EtcConfig.h"
+#include "EtcBlock4x4Encoding_RGB8.h"
+
+#include "EtcBlock4x4EncodingBits.h"
+#include "EtcBlock4x4.h"
+#include "EtcMath.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <float.h>
+#include <limits>
+
+namespace Etc
+{
+	float Block4x4Encoding_RGB8::s_afTHDistanceTable[TH_DISTANCES] =
+	{
+		3.0f / 255.0f,
+		6.0f / 255.0f,
+		11.0f / 255.0f,
+		16.0f / 255.0f,
+		23.0f / 255.0f,
+		32.0f / 255.0f,
+		41.0f / 255.0f,
+		64.0f / 255.0f
+	};
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+	Block4x4Encoding_RGB8::Block4x4Encoding_RGB8(void)
+	{
+
+		m_pencodingbitsRGB8 = nullptr;
+
+	}
+
+	Block4x4Encoding_RGB8::~Block4x4Encoding_RGB8(void) {}
+	// ----------------------------------------------------------------------------------------------------
+	// initialization from the encoding bits of a previous encoding
+	// a_pblockParent points to the block associated with this encoding
+	// a_errormetric is used to choose the best encoding
+	// a_pafrgbaSource points to a 4x4 block subset of the source image
+	// a_paucEncodingBits points to the final encoding bits of a previous encoding
+	//
+	void Block4x4Encoding_RGB8::InitFromEncodingBits(Block4x4 *a_pblockParent,
+														unsigned char *a_paucEncodingBits,
+														ColorFloatRGBA *a_pafrgbaSource,
+														ErrorMetric a_errormetric)
+	{
+		
+		// handle ETC1 modes
+		Block4x4Encoding_ETC1::InitFromEncodingBits(a_pblockParent,
+													a_paucEncodingBits, a_pafrgbaSource,a_errormetric);
+
+		m_pencodingbitsRGB8 = (Block4x4EncodingBits_RGB8 *)a_paucEncodingBits;
+
+		// detect if there is a T, H or Planar mode present
+		if (m_pencodingbitsRGB8->differential.diff)
+		{
+			int iRed1 = (int)m_pencodingbitsRGB8->differential.red1;
+			int iDRed2 = m_pencodingbitsRGB8->differential.dred2;
+			int iRed2 = iRed1 + iDRed2;
+
+			int iGreen1 = (int)m_pencodingbitsRGB8->differential.green1;
+			int iDGreen2 = m_pencodingbitsRGB8->differential.dgreen2;
+			int iGreen2 = iGreen1 + iDGreen2;
+
+			int iBlue1 = (int)m_pencodingbitsRGB8->differential.blue1;
+			int iDBlue2 = m_pencodingbitsRGB8->differential.dblue2;
+			int iBlue2 = iBlue1 + iDBlue2;
+
+			if (iRed2 < 0 || iRed2 > 31)
+			{
+				InitFromEncodingBits_T();
+			}
+			else if (iGreen2 < 0 || iGreen2 > 31)
+			{
+				InitFromEncodingBits_H();
+			}
+			else if (iBlue2 < 0 || iBlue2 > 31)
+			{
+				InitFromEncodingBits_Planar();
+			}
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// initialization from the encoding bits of a previous encoding if T mode is detected
+	//
+	void Block4x4Encoding_RGB8::InitFromEncodingBits_T(void)
+	{
+
+		m_mode = MODE_T;
+
+		unsigned char ucRed1 = (unsigned char)((m_pencodingbitsRGB8->t.red1a << 2) +
+								m_pencodingbitsRGB8->t.red1b);
+		unsigned char ucGreen1 = m_pencodingbitsRGB8->t.green1;
+		unsigned char ucBlue1 = m_pencodingbitsRGB8->t.blue1;
+
+		unsigned char ucRed2 = m_pencodingbitsRGB8->t.red2;
+		unsigned char ucGreen2 = m_pencodingbitsRGB8->t.green2;
+		unsigned char ucBlue2 = m_pencodingbitsRGB8->t.blue2;
+
+		m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4(ucRed1, ucGreen1, ucBlue1);
+		m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4(ucRed2, ucGreen2, ucBlue2);
+
+		m_uiCW1 = (m_pencodingbitsRGB8->t.da << 1) + m_pencodingbitsRGB8->t.db;
+
+		Block4x4Encoding_ETC1::InitFromEncodingBits_Selectors();
+
+		DecodePixels_T();
+
+		CalcBlockError();
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// initialization from the encoding bits of a previous encoding if H mode is detected
+	//
+	void Block4x4Encoding_RGB8::InitFromEncodingBits_H(void)
+	{
+
+		m_mode = MODE_H;
+		
+		unsigned char ucRed1 = m_pencodingbitsRGB8->h.red1;
+		unsigned char ucGreen1 = (unsigned char)((m_pencodingbitsRGB8->h.green1a << 1) +
+									m_pencodingbitsRGB8->h.green1b);
+		unsigned char ucBlue1 = (unsigned char)((m_pencodingbitsRGB8->h.blue1a << 3) +
+								(m_pencodingbitsRGB8->h.blue1b << 1) + 
+								m_pencodingbitsRGB8->h.blue1c);
+
+		unsigned char ucRed2 = m_pencodingbitsRGB8->h.red2;
+		unsigned char ucGreen2 = (unsigned char)((m_pencodingbitsRGB8->h.green2a << 1) +
+									m_pencodingbitsRGB8->h.green2b);
+		unsigned char ucBlue2 = m_pencodingbitsRGB8->h.blue2;
+
+		m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4(ucRed1, ucGreen1, ucBlue1);
+		m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4(ucRed2, ucGreen2, ucBlue2);
+
+		// used to determine the LSB of the CW
+		unsigned int uiRGB1 = (unsigned int)(((int)ucRed1 << 16) + ((int)ucGreen1 << 8) + (int)ucBlue1);
+		unsigned int uiRGB2 = (unsigned int)(((int)ucRed2 << 16) + ((int)ucGreen2 << 8) + (int)ucBlue2);
+
+		m_uiCW1 = (m_pencodingbitsRGB8->h.da << 2) + (m_pencodingbitsRGB8->h.db << 1);
+		if (uiRGB1 >= uiRGB2)
+		{
+			m_uiCW1++;
+		}
+
+		Block4x4Encoding_ETC1::InitFromEncodingBits_Selectors();
+
+		DecodePixels_H();
+
+		CalcBlockError();
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// initialization from the encoding bits of a previous encoding if Planar mode is detected
+	//
+	void Block4x4Encoding_RGB8::InitFromEncodingBits_Planar(void)
+	{
+
+		m_mode = MODE_PLANAR;
+
+		unsigned char ucOriginRed = m_pencodingbitsRGB8->planar.originRed;
+		unsigned char ucOriginGreen = (unsigned char)((m_pencodingbitsRGB8->planar.originGreen1 << 6) +
+										m_pencodingbitsRGB8->planar.originGreen2);
+		unsigned char ucOriginBlue = (unsigned char)((m_pencodingbitsRGB8->planar.originBlue1 << 5) +
+										(m_pencodingbitsRGB8->planar.originBlue2 << 3) +
+										(m_pencodingbitsRGB8->planar.originBlue3 << 1) +
+										m_pencodingbitsRGB8->planar.originBlue4);
+
+		unsigned char ucHorizRed = (unsigned char)((m_pencodingbitsRGB8->planar.horizRed1 << 1) +
+									m_pencodingbitsRGB8->planar.horizRed2);
+		unsigned char ucHorizGreen = m_pencodingbitsRGB8->planar.horizGreen;
+		unsigned char ucHorizBlue = (unsigned char)((m_pencodingbitsRGB8->planar.horizBlue1 << 5) +
+									m_pencodingbitsRGB8->planar.horizBlue2);
+
+		unsigned char ucVertRed = (unsigned char)((m_pencodingbitsRGB8->planar.vertRed1 << 3) +
+									m_pencodingbitsRGB8->planar.vertRed2);
+		unsigned char ucVertGreen = (unsigned char)((m_pencodingbitsRGB8->planar.vertGreen1 << 2) +
+									m_pencodingbitsRGB8->planar.vertGreen2);
+		unsigned char ucVertBlue = m_pencodingbitsRGB8->planar.vertBlue;
+
+		m_frgbaColor1 = ColorFloatRGBA::ConvertFromR6G7B6(ucOriginRed, ucOriginGreen, ucOriginBlue);
+		m_frgbaColor2 = ColorFloatRGBA::ConvertFromR6G7B6(ucHorizRed, ucHorizGreen, ucHorizBlue);
+		m_frgbaColor3 = ColorFloatRGBA::ConvertFromR6G7B6(ucVertRed, ucVertGreen, ucVertBlue);
+
+		DecodePixels_Planar();
+
+		CalcBlockError();
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// perform a single encoding iteration
+	// replace the encoding if a better encoding was found
+	// subsequent iterations generally take longer for each iteration
+	// set m_boolDone if encoding is perfect or encoding is finished based on a_fEffort
+	//
+	void Block4x4Encoding_RGB8::PerformIteration(float a_fEffort)
+	{
+		assert(!m_boolDone);
+
+		switch (m_uiEncodingIterations)
+		{
+		case 0:
+			Block4x4Encoding_ETC1::PerformFirstIteration();
+			if (m_boolDone)
+			{
+				break;
+			}
+			TryPlanar(0);
+			SetDoneIfPerfect();
+			if (m_boolDone)
+			{
+				break;
+			}
+			TryTAndH(0);
+			break;
+
+		case 1:
+			Block4x4Encoding_ETC1::TryDifferential(m_boolMostLikelyFlip, 1, 0, 0);
+			break;
+
+		case 2:
+			Block4x4Encoding_ETC1::TryIndividual(m_boolMostLikelyFlip, 1);
+			break;
+
+		case 3:
+			Block4x4Encoding_ETC1::TryDifferential(!m_boolMostLikelyFlip, 1, 0, 0);
+			break;
+
+		case 4:
+			Block4x4Encoding_ETC1::TryIndividual(!m_boolMostLikelyFlip, 1);
+			break;
+
+		case 5:
+			TryPlanar(1);
+			if (a_fEffort <= 49.5f)
+			{
+				m_boolDone = true;
+			}
+			break;
+
+		case 6:
+			TryTAndH(1);
+			if (a_fEffort <= 59.5f)
+			{
+				m_boolDone = true;
+			}
+			break;
+
+		case 7:
+			Block4x4Encoding_ETC1::TryDegenerates1();
+			if (a_fEffort <= 69.5f)
+			{
+				m_boolDone = true;
+			}
+			break;
+
+		case 8:
+			Block4x4Encoding_ETC1::TryDegenerates2();
+			if (a_fEffort <= 79.5f)
+			{
+				m_boolDone = true;
+			}
+			break;
+
+		case 9:
+			Block4x4Encoding_ETC1::TryDegenerates3();
+			if (a_fEffort <= 89.5f)
+			{
+				m_boolDone = true;
+			}
+			break;
+
+		case 10:
+			Block4x4Encoding_ETC1::TryDegenerates4();
+			m_boolDone = true;
+			break;
+
+		default:
+			assert(0);
+			break;
+		}
+
+		m_uiEncodingIterations++;
+
+		SetDoneIfPerfect();
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// try encoding in Planar mode
+	// save this encoding if it improves the error
+	//
+	void Block4x4Encoding_RGB8::TryPlanar(unsigned int a_uiRadius)
+	{
+		Block4x4Encoding_RGB8 encodingTry = *this;
+
+		// init "try"
+		{
+			encodingTry.m_mode = MODE_PLANAR;
+			encodingTry.m_boolDiff = true;
+			encodingTry.m_boolFlip = false;
+		}
+
+		encodingTry.CalculatePlanarCornerColors();
+
+		encodingTry.DecodePixels_Planar();
+
+		encodingTry.CalcBlockError();
+
+		if (a_uiRadius > 0)
+		{
+			encodingTry.TwiddlePlanar();
+		}
+
+		if (encodingTry.m_fError < m_fError)
+		{
+			m_mode = MODE_PLANAR;
+			m_boolDiff = true;
+			m_boolFlip = false;
+			m_frgbaColor1 = encodingTry.m_frgbaColor1;
+			m_frgbaColor2 = encodingTry.m_frgbaColor2;
+			m_frgbaColor3 = encodingTry.m_frgbaColor3;
+
+			for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+			{
+				m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel];
+			}
+
+			m_fError = encodingTry.m_fError;
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// try encoding in T mode or H mode
+	// save this encoding if it improves the error
+	//
+	void Block4x4Encoding_RGB8::TryTAndH(unsigned int a_uiRadius)
+	{
+
+		CalculateBaseColorsForTAndH();
+
+		TryT(a_uiRadius);
+
+		TryH(a_uiRadius);
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// calculate original values for base colors
+	// store them in m_frgbaOriginalColor1 and m_frgbaOriginalColor2
+	//
+	void Block4x4Encoding_RGB8::CalculateBaseColorsForTAndH(void)
+	{
+
+		bool boolRGBX = m_pblockParent->GetImageSource()->GetErrorMetric() == ErrorMetric::RGBX;
+
+		ColorFloatRGBA frgbaBlockAverage = (m_frgbaSourceAverageLeft + m_frgbaSourceAverageRight) * 0.5f;
+
+		// find pixel farthest from average gray line
+		unsigned int uiFarthestPixel = 0;
+		float fFarthestGrayDistance2 = 0.0f;
+		unsigned int uiTransparentPixels = 0;
+		for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+		{
+			// don't count transparent
+			if (m_pafrgbaSource[uiPixel].fA == 0.0f && !boolRGBX)
+			{
+				uiTransparentPixels++;
+			}
+			else
+			{
+				float fGrayDistance2 = CalcGrayDistance2(m_pafrgbaSource[uiPixel], frgbaBlockAverage);
+
+				if (fGrayDistance2 > fFarthestGrayDistance2)
+				{
+					uiFarthestPixel = uiPixel;
+					fFarthestGrayDistance2 = fGrayDistance2;
+				}
+			}
+		}
+		// a transparent block should not reach this method
+		assert(uiTransparentPixels < PIXELS);
+
+		// set the original base colors to:
+		//		half way to the farthest pixel and
+		//		the mirror color on the other side of the average
+		ColorFloatRGBA frgbaOffset = (m_pafrgbaSource[uiFarthestPixel] - frgbaBlockAverage) * 0.5f;
+		m_frgbaOriginalColor1_TAndH = (frgbaBlockAverage + frgbaOffset).QuantizeR4G4B4();
+		m_frgbaOriginalColor2_TAndH = (frgbaBlockAverage - frgbaOffset).ClampRGB().QuantizeR4G4B4();	// the "other side" might be out of range
+
+		// move base colors to find best fit
+		for (unsigned int uiIteration = 0; uiIteration < 10; uiIteration++)
+		{
+			// find the center of pixels closest to each color
+			float fPixelsCloserToColor1 = 0.0f;
+			ColorFloatRGBA frgbSumPixelsCloserToColor1;
+			float fPixelsCloserToColor2 = 0.0f;
+			ColorFloatRGBA frgbSumPixelsCloserToColor2;
+			for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+			{
+				// don't count transparent pixels
+				if (m_pafrgbaSource[uiPixel].fA == 0.0f)
+				{
+					continue;
+				}
+
+				float fGrayDistance2ToColor1 = CalcGrayDistance2(m_pafrgbaSource[uiPixel], m_frgbaOriginalColor1_TAndH);
+				float fGrayDistance2ToColor2 = CalcGrayDistance2(m_pafrgbaSource[uiPixel], m_frgbaOriginalColor2_TAndH);
+
+				ColorFloatRGBA frgbaAlphaWeightedSource = m_pafrgbaSource[uiPixel] * m_pafrgbaSource[uiPixel].fA;
+					
+				if (fGrayDistance2ToColor1 <= fGrayDistance2ToColor2)
+				{
+					fPixelsCloserToColor1 += m_pafrgbaSource[uiPixel].fA;
+					frgbSumPixelsCloserToColor1 = frgbSumPixelsCloserToColor1 + frgbaAlphaWeightedSource;
+				}
+				else
+				{
+					fPixelsCloserToColor2 += m_pafrgbaSource[uiPixel].fA;
+					frgbSumPixelsCloserToColor2 = frgbSumPixelsCloserToColor2 + frgbaAlphaWeightedSource;
+				}
+			}
+			if (fPixelsCloserToColor1 == 0.0f || fPixelsCloserToColor2 == 0.0f)
+			{
+				break;
+			}
+
+			ColorFloatRGBA frgbAvgColor1Pixels = (frgbSumPixelsCloserToColor1 * (1.0f / fPixelsCloserToColor1)).QuantizeR4G4B4();
+			ColorFloatRGBA frgbAvgColor2Pixels = (frgbSumPixelsCloserToColor2 * (1.0f / fPixelsCloserToColor2)).QuantizeR4G4B4();
+
+			if (frgbAvgColor1Pixels.fR == m_frgbaOriginalColor1_TAndH.fR &&
+				frgbAvgColor1Pixels.fG == m_frgbaOriginalColor1_TAndH.fG &&
+				frgbAvgColor1Pixels.fB == m_frgbaOriginalColor1_TAndH.fB &&
+				frgbAvgColor2Pixels.fR == m_frgbaOriginalColor2_TAndH.fR &&
+				frgbAvgColor2Pixels.fG == m_frgbaOriginalColor2_TAndH.fG &&
+				frgbAvgColor2Pixels.fB == m_frgbaOriginalColor2_TAndH.fB)
+			{
+				break;
+			}
+
+			m_frgbaOriginalColor1_TAndH = frgbAvgColor1Pixels;
+			m_frgbaOriginalColor2_TAndH = frgbAvgColor2Pixels;
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// try encoding in T mode
+	// save this encoding if it improves the error
+	//
+	// since pixels that use base color1 don't use the distance table, color1 and color2 can be twiddled independently
+	// better encoding can be found if TWIDDLE_RADIUS is set to 2, but it will be much slower
+	//
+	void Block4x4Encoding_RGB8::TryT(unsigned int a_uiRadius)
+	{
+		Block4x4Encoding_RGB8 encodingTry = *this;
+
+		// init "try"
+		{
+			encodingTry.m_mode = MODE_T;
+			encodingTry.m_boolDiff = true;
+			encodingTry.m_boolFlip = false;
+			encodingTry.m_fError = FLT_MAX;
+		}
+
+		int iColor1Red = m_frgbaOriginalColor1_TAndH.IntRed(15.0f);
+		int iColor1Green = m_frgbaOriginalColor1_TAndH.IntGreen(15.0f);
+		int iColor1Blue = m_frgbaOriginalColor1_TAndH.IntBlue(15.0f);
+
+		int iMinRed1 = iColor1Red - (int)a_uiRadius;
+		if (iMinRed1 < 0)
+		{
+			iMinRed1 = 0;
+		}
+		int iMaxRed1 = iColor1Red + (int)a_uiRadius;
+		if (iMaxRed1 > 15)
+		{
+			iMinRed1 = 15;
+		}
+
+		int iMinGreen1 = iColor1Green - (int)a_uiRadius;
+		if (iMinGreen1 < 0)
+		{
+			iMinGreen1 = 0;
+		}
+		int iMaxGreen1 = iColor1Green + (int)a_uiRadius;
+		if (iMaxGreen1 > 15)
+		{
+			iMinGreen1 = 15;
+		}
+
+		int iMinBlue1 = iColor1Blue - (int)a_uiRadius;
+		if (iMinBlue1 < 0)
+		{
+			iMinBlue1 = 0;
+		}
+		int iMaxBlue1 = iColor1Blue + (int)a_uiRadius;
+		if (iMaxBlue1 > 15)
+		{
+			iMinBlue1 = 15;
+		}
+
+		int iColor2Red = m_frgbaOriginalColor2_TAndH.IntRed(15.0f);
+		int iColor2Green = m_frgbaOriginalColor2_TAndH.IntGreen(15.0f);
+		int iColor2Blue = m_frgbaOriginalColor2_TAndH.IntBlue(15.0f);
+
+		int iMinRed2 = iColor2Red - (int)a_uiRadius;
+		if (iMinRed2 < 0)
+		{
+			iMinRed2 = 0;
+		}
+		int iMaxRed2 = iColor2Red + (int)a_uiRadius;
+		if (iMaxRed2 > 15)
+		{
+			iMinRed2 = 15;
+		}
+
+		int iMinGreen2 = iColor2Green - (int)a_uiRadius;
+		if (iMinGreen2 < 0)
+		{
+			iMinGreen2 = 0;
+		}
+		int iMaxGreen2 = iColor2Green + (int)a_uiRadius;
+		if (iMaxGreen2 > 15)
+		{
+			iMinGreen2 = 15;
+		}
+
+		int iMinBlue2 = iColor2Blue - (int)a_uiRadius;
+		if (iMinBlue2 < 0)
+		{
+			iMinBlue2 = 0;
+		}
+		int iMaxBlue2 = iColor2Blue + (int)a_uiRadius;
+		if (iMaxBlue2 > 15)
+		{
+			iMinBlue2 = 15;
+		}
+
+		for (unsigned int uiDistance = 0; uiDistance < TH_DISTANCES; uiDistance++)
+		{
+			encodingTry.m_uiCW1 = uiDistance;
+
+			// twiddle m_frgbaOriginalColor2_TAndH
+			// twiddle color2 first, since it affects 3 selectors, while color1 only affects one selector
+			//
+			for (int iRed2 = iMinRed2; iRed2 <= iMaxRed2; iRed2++)
+			{
+				for (int iGreen2 = iMinGreen2; iGreen2 <= iMaxGreen2; iGreen2++)
+				{
+					for (int iBlue2 = iMinBlue2; iBlue2 <= iMaxBlue2; iBlue2++)
+					{
+						for (unsigned int uiBaseColorSwaps = 0; uiBaseColorSwaps < 2; uiBaseColorSwaps++)
+						{
+							if (uiBaseColorSwaps == 0)
+							{
+								encodingTry.m_frgbaColor1 = m_frgbaOriginalColor1_TAndH;
+								encodingTry.m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed2, (unsigned char)iGreen2, (unsigned char)iBlue2);
+							}
+							else
+							{
+								encodingTry.m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed2, (unsigned char)iGreen2, (unsigned char)iBlue2);
+								encodingTry.m_frgbaColor2 = m_frgbaOriginalColor1_TAndH;
+							}
+
+							encodingTry.TryT_BestSelectorCombination();
+
+							if (encodingTry.m_fError < m_fError)
+							{
+								m_mode = encodingTry.m_mode;
+								m_boolDiff = encodingTry.m_boolDiff;
+								m_boolFlip = encodingTry.m_boolFlip;
+
+								m_frgbaColor1 = encodingTry.m_frgbaColor1;
+								m_frgbaColor2 = encodingTry.m_frgbaColor2;
+								m_uiCW1 = encodingTry.m_uiCW1;
+
+								for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+								{
+									m_auiSelectors[uiPixel] = encodingTry.m_auiSelectors[uiPixel];
+									m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel];
+								}
+
+								m_fError = encodingTry.m_fError;
+							}
+						}
+					}
+				}
+			}
+
+			// twiddle m_frgbaOriginalColor1_TAndH
+			for (int iRed1 = iMinRed1; iRed1 <= iMaxRed1; iRed1++)
+			{
+				for (int iGreen1 = iMinGreen1; iGreen1 <= iMaxGreen1; iGreen1++)
+				{
+					for (int iBlue1 = iMinBlue1; iBlue1 <= iMaxBlue1; iBlue1++)
+					{
+						for (unsigned int uiBaseColorSwaps = 0; uiBaseColorSwaps < 2; uiBaseColorSwaps++)
+						{
+							if (uiBaseColorSwaps == 0)
+							{
+								encodingTry.m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed1, (unsigned char)iGreen1, (unsigned char)iBlue1);
+								encodingTry.m_frgbaColor2 = m_frgbaOriginalColor2_TAndH;
+							}
+							else
+							{
+								encodingTry.m_frgbaColor1 = m_frgbaOriginalColor2_TAndH;
+								encodingTry.m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed1, (unsigned char)iGreen1, (unsigned char)iBlue1);
+							}
+
+							encodingTry.TryT_BestSelectorCombination();
+
+							if (encodingTry.m_fError < m_fError)
+							{
+								m_mode = encodingTry.m_mode;
+								m_boolDiff = encodingTry.m_boolDiff;
+								m_boolFlip = encodingTry.m_boolFlip;
+
+								m_frgbaColor1 = encodingTry.m_frgbaColor1;
+								m_frgbaColor2 = encodingTry.m_frgbaColor2;
+								m_uiCW1 = encodingTry.m_uiCW1;
+
+								for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+								{
+									m_auiSelectors[uiPixel] = encodingTry.m_auiSelectors[uiPixel];
+									m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel];
+								}
+
+								m_fError = encodingTry.m_fError;
+							}
+						}
+					}
+				}
+			}
+
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// find best selector combination for TryT
+	// called on an encodingTry
+	//
+	void Block4x4Encoding_RGB8::TryT_BestSelectorCombination(void)
+	{
+
+		float fDistance = s_afTHDistanceTable[m_uiCW1];
+
+		unsigned int auiBestPixelSelectors[PIXELS];
+		float afBestPixelErrors[PIXELS] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX,
+			FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX };
+		ColorFloatRGBA	afrgbaBestDecodedPixels[PIXELS];
+		ColorFloatRGBA afrgbaDecodedPixel[SELECTORS];
+		
+		assert(SELECTORS == 4);
+		afrgbaDecodedPixel[0] = m_frgbaColor1;
+		afrgbaDecodedPixel[1] = (m_frgbaColor2 + fDistance).ClampRGB();
+		afrgbaDecodedPixel[2] = m_frgbaColor2;
+		afrgbaDecodedPixel[3] = (m_frgbaColor2 - fDistance).ClampRGB();
+		
+		// try each selector
+		for (unsigned int uiSelector = 0; uiSelector < SELECTORS; uiSelector++)
+		{
+			for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+			{
+
+				float fPixelError = CalcPixelError(afrgbaDecodedPixel[uiSelector], m_afDecodedAlphas[uiPixel],
+														m_pafrgbaSource[uiPixel]);
+
+				if (fPixelError < afBestPixelErrors[uiPixel])
+				{
+					afBestPixelErrors[uiPixel] = fPixelError;
+					auiBestPixelSelectors[uiPixel] = uiSelector;
+					afrgbaBestDecodedPixels[uiPixel] = afrgbaDecodedPixel[uiSelector];
+				}
+			}
+		}
+		
+
+		// add up all of the pixel errors
+		float fBlockError = 0.0f;
+		for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+		{
+			fBlockError += afBestPixelErrors[uiPixel];
+		}
+
+		if (fBlockError < m_fError)
+		{
+			m_fError = fBlockError;
+
+			for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+			{
+				m_auiSelectors[uiPixel] = auiBestPixelSelectors[uiPixel];
+				m_afrgbaDecodedColors[uiPixel] = afrgbaBestDecodedPixels[uiPixel];
+			}
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// try encoding in T mode
+	// save this encoding if it improves the error
+	//
+	// since all pixels use the distance table, color1 and color2 can NOT be twiddled independently
+	// TWIDDLE_RADIUS of 2 is WAY too slow
+	//
+	void Block4x4Encoding_RGB8::TryH(unsigned int a_uiRadius)
+	{
+		Block4x4Encoding_RGB8 encodingTry = *this;
+
+		// init "try"
+		{
+			encodingTry.m_mode = MODE_H;
+			encodingTry.m_boolDiff = true;
+			encodingTry.m_boolFlip = false;
+			encodingTry.m_fError = FLT_MAX;
+		}
+
+		int iColor1Red = m_frgbaOriginalColor1_TAndH.IntRed(15.0f);
+		int iColor1Green = m_frgbaOriginalColor1_TAndH.IntGreen(15.0f);
+		int iColor1Blue = m_frgbaOriginalColor1_TAndH.IntBlue(15.0f);
+
+		int iMinRed1 = iColor1Red - (int)a_uiRadius;
+		if (iMinRed1 < 0)
+		{
+			iMinRed1 = 0;
+		}
+		int iMaxRed1 = iColor1Red + (int)a_uiRadius;
+		if (iMaxRed1 > 15)
+		{
+			iMinRed1 = 15;
+		}
+
+		int iMinGreen1 = iColor1Green - (int)a_uiRadius;
+		if (iMinGreen1 < 0)
+		{
+			iMinGreen1 = 0;
+		}
+		int iMaxGreen1 = iColor1Green + (int)a_uiRadius;
+		if (iMaxGreen1 > 15)
+		{
+			iMinGreen1 = 15;
+		}
+
+		int iMinBlue1 = iColor1Blue - (int)a_uiRadius;
+		if (iMinBlue1 < 0)
+		{
+			iMinBlue1 = 0;
+		}
+		int iMaxBlue1 = iColor1Blue + (int)a_uiRadius;
+		if (iMaxBlue1 > 15)
+		{
+			iMinBlue1 = 15;
+		}
+
+		int iColor2Red = m_frgbaOriginalColor2_TAndH.IntRed(15.0f);
+		int iColor2Green = m_frgbaOriginalColor2_TAndH.IntGreen(15.0f);
+		int iColor2Blue = m_frgbaOriginalColor2_TAndH.IntBlue(15.0f);
+
+		int iMinRed2 = iColor2Red - (int)a_uiRadius;
+		if (iMinRed2 < 0)
+		{
+			iMinRed2 = 0;
+		}
+		int iMaxRed2 = iColor2Red + (int)a_uiRadius;
+		if (iMaxRed2 > 15)
+		{
+			iMinRed2 = 15;
+		}
+
+		int iMinGreen2 = iColor2Green - (int)a_uiRadius;
+		if (iMinGreen2 < 0)
+		{
+			iMinGreen2 = 0;
+		}
+		int iMaxGreen2 = iColor2Green + (int)a_uiRadius;
+		if (iMaxGreen2 > 15)
+		{
+			iMinGreen2 = 15;
+		}
+
+		int iMinBlue2 = iColor2Blue - (int)a_uiRadius;
+		if (iMinBlue2 < 0)
+		{
+			iMinBlue2 = 0;
+		}
+		int iMaxBlue2 = iColor2Blue + (int)a_uiRadius;
+		if (iMaxBlue2 > 15)
+		{
+			iMinBlue2 = 15;
+		}
+
+		for (unsigned int uiDistance = 0; uiDistance < TH_DISTANCES; uiDistance++)
+		{
+			encodingTry.m_uiCW1 = uiDistance;
+
+			// twiddle m_frgbaOriginalColor1_TAndH
+			for (int iRed1 = iMinRed1; iRed1 <= iMaxRed1; iRed1++)
+			{
+				for (int iGreen1 = iMinGreen1; iGreen1 <= iMaxGreen1; iGreen1++)
+				{
+					for (int iBlue1 = iMinBlue1; iBlue1 <= iMaxBlue1; iBlue1++)
+					{
+						encodingTry.m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed1, (unsigned char)iGreen1, (unsigned char)iBlue1);
+						encodingTry.m_frgbaColor2 = m_frgbaOriginalColor2_TAndH;
+
+						// if color1 == color2, H encoding issues can pop up, so abort
+						if (iRed1 == iColor2Red && iGreen1 == iColor2Green && iBlue1 == iColor2Blue)
+						{
+							continue;
+						}
+
+						encodingTry.TryH_BestSelectorCombination();
+
+						if (encodingTry.m_fError < m_fError)
+						{
+							m_mode = encodingTry.m_mode;
+							m_boolDiff = encodingTry.m_boolDiff;
+							m_boolFlip = encodingTry.m_boolFlip;
+
+							m_frgbaColor1 = encodingTry.m_frgbaColor1;
+							m_frgbaColor2 = encodingTry.m_frgbaColor2;
+							m_uiCW1 = encodingTry.m_uiCW1;
+
+							for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+							{
+								m_auiSelectors[uiPixel] = encodingTry.m_auiSelectors[uiPixel];
+								m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel];
+							}
+
+							m_fError = encodingTry.m_fError;
+						}
+					}
+				}
+			}
+
+			// twiddle m_frgbaOriginalColor2_TAndH
+			for (int iRed2 = iMinRed2; iRed2 <= iMaxRed2; iRed2++)
+			{
+				for (int iGreen2 = iMinGreen2; iGreen2 <= iMaxGreen2; iGreen2++)
+				{
+					for (int iBlue2 = iMinBlue2; iBlue2 <= iMaxBlue2; iBlue2++)
+					{
+						encodingTry.m_frgbaColor1 = m_frgbaOriginalColor1_TAndH;
+						encodingTry.m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed2, (unsigned char)iGreen2, (unsigned char)iBlue2);
+
+						// if color1 == color2, H encoding issues can pop up, so abort
+						if (iRed2 == iColor1Red && iGreen2 == iColor1Green && iBlue2 == iColor1Blue)
+						{
+							continue;
+						}
+
+						encodingTry.TryH_BestSelectorCombination();
+
+						if (encodingTry.m_fError < m_fError)
+						{
+							m_mode = encodingTry.m_mode;
+							m_boolDiff = encodingTry.m_boolDiff;
+							m_boolFlip = encodingTry.m_boolFlip;
+
+							m_frgbaColor1 = encodingTry.m_frgbaColor1;
+							m_frgbaColor2 = encodingTry.m_frgbaColor2;
+							m_uiCW1 = encodingTry.m_uiCW1;
+
+							for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+							{
+								m_auiSelectors[uiPixel] = encodingTry.m_auiSelectors[uiPixel];
+								m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel];
+							}
+
+							m_fError = encodingTry.m_fError;
+						}
+					}
+				}
+			}
+
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// find best selector combination for TryH
+	// called on an encodingTry
+	//
+	void Block4x4Encoding_RGB8::TryH_BestSelectorCombination(void)
+	{
+
+		float fDistance = s_afTHDistanceTable[m_uiCW1];
+
+		unsigned int auiBestPixelSelectors[PIXELS];
+		float afBestPixelErrors[PIXELS] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX,
+			FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX };
+		ColorFloatRGBA	afrgbaBestDecodedPixels[PIXELS];
+		ColorFloatRGBA afrgbaDecodedPixel[SELECTORS];
+		
+		assert(SELECTORS == 4);
+		afrgbaDecodedPixel[0] = (m_frgbaColor1 + fDistance).ClampRGB();
+		afrgbaDecodedPixel[1] = (m_frgbaColor1 - fDistance).ClampRGB();
+		afrgbaDecodedPixel[2] = (m_frgbaColor2 + fDistance).ClampRGB();
+		afrgbaDecodedPixel[3] = (m_frgbaColor2 - fDistance).ClampRGB();
+		
+		// try each selector
+		for (unsigned int uiSelector = 0; uiSelector < SELECTORS; uiSelector++)
+		{
+			for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+			{
+
+				float fPixelError = CalcPixelError(afrgbaDecodedPixel[uiSelector], m_afDecodedAlphas[uiPixel],
+														m_pafrgbaSource[uiPixel]);
+
+				if (fPixelError < afBestPixelErrors[uiPixel])
+				{
+					afBestPixelErrors[uiPixel] = fPixelError;
+					auiBestPixelSelectors[uiPixel] = uiSelector;
+					afrgbaBestDecodedPixels[uiPixel] = afrgbaDecodedPixel[uiSelector];
+				}
+			}
+		}
+		
+
+		// add up all of the pixel errors
+		float fBlockError = 0.0f;
+		for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+		{
+			fBlockError += afBestPixelErrors[uiPixel];
+		}
+
+		if (fBlockError < m_fError)
+		{
+			m_fError = fBlockError;
+
+			for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+			{
+				m_auiSelectors[uiPixel] = auiBestPixelSelectors[uiPixel];
+				m_afrgbaDecodedColors[uiPixel] = afrgbaBestDecodedPixels[uiPixel];
+			}
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// use linear regression to find the best fit for colors along the edges of the 4x4 block
+	//
+	void Block4x4Encoding_RGB8::CalculatePlanarCornerColors(void)
+	{
+		ColorFloatRGBA afrgbaRegression[MAX_PLANAR_REGRESSION_SIZE];
+		ColorFloatRGBA frgbaSlope;
+		ColorFloatRGBA frgbaOffset;
+
+		// top edge
+		afrgbaRegression[0] = m_pafrgbaSource[0];
+		afrgbaRegression[1] = m_pafrgbaSource[4];
+		afrgbaRegression[2] = m_pafrgbaSource[8];
+		afrgbaRegression[3] = m_pafrgbaSource[12];
+		ColorRegression(afrgbaRegression, 4, &frgbaSlope, &frgbaOffset);
+		m_frgbaColor1 = frgbaOffset;
+		m_frgbaColor2 = (frgbaSlope * 4.0f) + frgbaOffset;
+
+		// left edge
+		afrgbaRegression[0] = m_pafrgbaSource[0];
+		afrgbaRegression[1] = m_pafrgbaSource[1];
+		afrgbaRegression[2] = m_pafrgbaSource[2];
+		afrgbaRegression[3] = m_pafrgbaSource[3];
+		ColorRegression(afrgbaRegression, 4, &frgbaSlope, &frgbaOffset);
+		m_frgbaColor1 = (m_frgbaColor1 + frgbaOffset) * 0.5f;		// average with top edge
+		m_frgbaColor3 = (frgbaSlope * 4.0f) + frgbaOffset;
+
+		// right edge
+		afrgbaRegression[0] = m_pafrgbaSource[12];
+		afrgbaRegression[1] = m_pafrgbaSource[13];
+		afrgbaRegression[2] = m_pafrgbaSource[14];
+		afrgbaRegression[3] = m_pafrgbaSource[15];
+		ColorRegression(afrgbaRegression, 4, &frgbaSlope, &frgbaOffset);
+		m_frgbaColor2 = (m_frgbaColor2 + frgbaOffset) * 0.5f;		// average with top edge
+
+		// bottom edge
+		afrgbaRegression[0] = m_pafrgbaSource[3];
+		afrgbaRegression[1] = m_pafrgbaSource[7];
+		afrgbaRegression[2] = m_pafrgbaSource[11];
+		afrgbaRegression[3] = m_pafrgbaSource[15];
+		ColorRegression(afrgbaRegression, 4, &frgbaSlope, &frgbaOffset);
+		m_frgbaColor3 = (m_frgbaColor3 + frgbaOffset) * 0.5f;		// average with left edge
+
+		// quantize corner colors to 6/7/6
+		m_frgbaColor1 = m_frgbaColor1.QuantizeR6G7B6();
+		m_frgbaColor2 = m_frgbaColor2.QuantizeR6G7B6();
+		m_frgbaColor3 = m_frgbaColor3.QuantizeR6G7B6();
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// try different corner colors by slightly changing R, G and B independently
+	//
+	// R, G and B decoding and errors are independent, so R, G and B twiddles can be independent
+	//
+	// return true if improvement
+	//
+	bool Block4x4Encoding_RGB8::TwiddlePlanar(void)
+	{
+		bool boolImprovement = false;
+
+		while (TwiddlePlanarR())
+		{
+			boolImprovement = true;
+		}
+
+		while (TwiddlePlanarG())
+		{
+			boolImprovement = true;
+		}
+
+		while (TwiddlePlanarB())
+		{
+			boolImprovement = true;
+		}
+
+		return boolImprovement;
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// try different corner colors by slightly changing R
+	//
+	bool Block4x4Encoding_RGB8::TwiddlePlanarR()
+	{
+		bool boolImprovement = false;
+
+		Block4x4Encoding_RGB8 encodingTry = *this;
+
+		// init "try"
+		{
+			encodingTry.m_mode = MODE_PLANAR;
+			encodingTry.m_boolDiff = true;
+			encodingTry.m_boolFlip = false;
+		}
+
+		int iOriginRed = encodingTry.m_frgbaColor1.IntRed(63.0f);
+		int iHorizRed = encodingTry.m_frgbaColor2.IntRed(63.0f);
+		int iVertRed = encodingTry.m_frgbaColor3.IntRed(63.0f);
+
+		for (int iTryOriginRed = iOriginRed - 1; iTryOriginRed <= iOriginRed + 1; iTryOriginRed++)
+		{
+			// check for out of range
+			if (iTryOriginRed < 0 || iTryOriginRed > 63)
+			{
+				continue;
+			}
+
+			encodingTry.m_frgbaColor1.fR = ((iTryOriginRed << 2) + (iTryOriginRed >> 4)) / 255.0f;
+
+			for (int iTryHorizRed = iHorizRed - 1; iTryHorizRed <= iHorizRed + 1; iTryHorizRed++)
+			{
+				// check for out of range
+				if (iTryHorizRed < 0 || iTryHorizRed > 63)
+				{
+					continue;
+				}
+
+				encodingTry.m_frgbaColor2.fR = ((iTryHorizRed << 2) + (iTryHorizRed >> 4)) / 255.0f;
+
+				for (int iTryVertRed = iVertRed - 1; iTryVertRed <= iVertRed + 1; iTryVertRed++)
+				{
+					// check for out of range
+					if (iTryVertRed < 0 || iTryVertRed > 63)
+					{
+						continue;
+					}
+
+					// don't bother with null twiddle
+					if (iTryOriginRed == iOriginRed && iTryHorizRed == iHorizRed && iTryVertRed == iVertRed)
+					{
+						continue;
+					}
+
+					encodingTry.m_frgbaColor3.fR = ((iTryVertRed << 2) + (iTryVertRed >> 4)) / 255.0f;
+
+					encodingTry.DecodePixels_Planar();
+
+					encodingTry.CalcBlockError();
+
+					if (encodingTry.m_fError < m_fError)
+					{
+						m_mode = MODE_PLANAR;
+						m_boolDiff = true;
+						m_boolFlip = false;
+						m_frgbaColor1 = encodingTry.m_frgbaColor1;
+						m_frgbaColor2 = encodingTry.m_frgbaColor2;
+						m_frgbaColor3 = encodingTry.m_frgbaColor3;
+
+						for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+						{
+							m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel];
+						}
+
+						m_fError = encodingTry.m_fError;
+
+						boolImprovement = true;
+					}
+				}
+			}
+		}
+
+		return boolImprovement;
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// try different corner colors by slightly changing G
+	//
+	bool Block4x4Encoding_RGB8::TwiddlePlanarG()
+	{
+		bool boolImprovement = false;
+
+		Block4x4Encoding_RGB8 encodingTry = *this;
+
+		// init "try"
+		{
+			encodingTry.m_mode = MODE_PLANAR;
+			encodingTry.m_boolDiff = true;
+			encodingTry.m_boolFlip = false;
+		}
+
+		int iOriginGreen = encodingTry.m_frgbaColor1.IntGreen(127.0f);
+		int iHorizGreen = encodingTry.m_frgbaColor2.IntGreen(127.0f);
+		int iVertGreen = encodingTry.m_frgbaColor3.IntGreen(127.0f);
+
+		for (int iTryOriginGreen = iOriginGreen - 1; iTryOriginGreen <= iOriginGreen + 1; iTryOriginGreen++)
+		{
+			// check for out of range
+			if (iTryOriginGreen < 0 || iTryOriginGreen > 127)
+			{
+				continue;
+			}
+
+			encodingTry.m_frgbaColor1.fG = ((iTryOriginGreen << 1) + (iTryOriginGreen >> 6)) / 255.0f;
+
+			for (int iTryHorizGreen = iHorizGreen - 1; iTryHorizGreen <= iHorizGreen + 1; iTryHorizGreen++)
+			{
+				// check for out of range
+				if (iTryHorizGreen < 0 || iTryHorizGreen > 127)
+				{
+					continue;
+				}
+
+				encodingTry.m_frgbaColor2.fG = ((iTryHorizGreen << 1) + (iTryHorizGreen >> 6)) / 255.0f;
+
+				for (int iTryVertGreen = iVertGreen - 1; iTryVertGreen <= iVertGreen + 1; iTryVertGreen++)
+				{
+					// check for out of range
+					if (iTryVertGreen < 0 || iTryVertGreen > 127)
+					{
+						continue;
+					}
+
+					// don't bother with null twiddle
+					if (iTryOriginGreen == iOriginGreen && 
+						iTryHorizGreen == iHorizGreen && 
+						iTryVertGreen == iVertGreen)
+					{
+						continue;
+					}
+
+					encodingTry.m_frgbaColor3.fG = ((iTryVertGreen << 1) + (iTryVertGreen >> 6)) / 255.0f;
+
+					encodingTry.DecodePixels_Planar();
+
+					encodingTry.CalcBlockError();
+
+					if (encodingTry.m_fError < m_fError)
+					{
+						m_mode = MODE_PLANAR;
+						m_boolDiff = true;
+						m_boolFlip = false;
+						m_frgbaColor1 = encodingTry.m_frgbaColor1;
+						m_frgbaColor2 = encodingTry.m_frgbaColor2;
+						m_frgbaColor3 = encodingTry.m_frgbaColor3;
+
+						for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+						{
+							m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel];
+						}
+
+						m_fError = encodingTry.m_fError;
+
+						boolImprovement = true;
+					}
+				}
+			}
+		}
+
+		return boolImprovement;
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// try different corner colors by slightly changing B
+	//
+	bool Block4x4Encoding_RGB8::TwiddlePlanarB()
+	{
+		bool boolImprovement = false;
+
+		Block4x4Encoding_RGB8 encodingTry = *this;
+
+		// init "try"
+		{
+			encodingTry.m_mode = MODE_PLANAR;
+			encodingTry.m_boolDiff = true;
+			encodingTry.m_boolFlip = false;
+		}
+
+		int iOriginBlue = encodingTry.m_frgbaColor1.IntBlue(63.0f);
+		int iHorizBlue = encodingTry.m_frgbaColor2.IntBlue(63.0f);
+		int iVertBlue = encodingTry.m_frgbaColor3.IntBlue(63.0f);
+
+		for (int iTryOriginBlue = iOriginBlue - 1; iTryOriginBlue <= iOriginBlue + 1; iTryOriginBlue++)
+		{
+			// check for out of range
+			if (iTryOriginBlue < 0 || iTryOriginBlue > 63)
+			{
+				continue;
+			}
+
+			encodingTry.m_frgbaColor1.fB = ((iTryOriginBlue << 2) + (iTryOriginBlue >> 4)) / 255.0f;
+
+			for (int iTryHorizBlue = iHorizBlue - 1; iTryHorizBlue <= iHorizBlue + 1; iTryHorizBlue++)
+			{
+				// check for out of range
+				if (iTryHorizBlue < 0 || iTryHorizBlue > 63)
+				{
+					continue;
+				}
+
+				encodingTry.m_frgbaColor2.fB = ((iTryHorizBlue << 2) + (iTryHorizBlue >> 4)) / 255.0f;
+
+				for (int iTryVertBlue = iVertBlue - 1; iTryVertBlue <= iVertBlue + 1; iTryVertBlue++)
+				{
+					// check for out of range
+					if (iTryVertBlue < 0 || iTryVertBlue > 63)
+					{
+						continue;
+					}
+
+					// don't bother with null twiddle
+					if (iTryOriginBlue == iOriginBlue && iTryHorizBlue == iHorizBlue && iTryVertBlue == iVertBlue)
+					{
+						continue;
+					}
+
+					encodingTry.m_frgbaColor3.fB = ((iTryVertBlue << 2) + (iTryVertBlue >> 4)) / 255.0f;
+
+					encodingTry.DecodePixels_Planar();
+
+					encodingTry.CalcBlockError();
+
+					if (encodingTry.m_fError < m_fError)
+					{
+						m_mode = MODE_PLANAR;
+						m_boolDiff = true;
+						m_boolFlip = false;
+						m_frgbaColor1 = encodingTry.m_frgbaColor1;
+						m_frgbaColor2 = encodingTry.m_frgbaColor2;
+						m_frgbaColor3 = encodingTry.m_frgbaColor3;
+
+						for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+						{
+							m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel];
+						}
+
+						m_fError = encodingTry.m_fError;
+
+						boolImprovement = true;
+					}
+				}
+			}
+		}
+
+		return boolImprovement;
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// set the encoding bits based on encoding state
+	//
+	void Block4x4Encoding_RGB8::SetEncodingBits(void)
+	{
+
+		switch (m_mode)
+		{
+		case MODE_ETC1:
+			Block4x4Encoding_ETC1::SetEncodingBits();
+			break;
+
+		case MODE_T:
+			SetEncodingBits_T();
+			break;
+
+		case MODE_H:
+			SetEncodingBits_H();
+			break;
+
+		case MODE_PLANAR:
+			SetEncodingBits_Planar();
+			break;
+
+		default:
+			assert(false);
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// set the encoding bits based on encoding state for T mode
+	//
+	void Block4x4Encoding_RGB8::SetEncodingBits_T(void)
+	{
+		static const bool SANITY_CHECK = true;
+
+		assert(m_mode == MODE_T);
+		assert(m_boolDiff == true);
+
+		unsigned int uiRed1 = (unsigned int)m_frgbaColor1.IntRed(15.0f);
+		unsigned int uiGreen1 = (unsigned int)m_frgbaColor1.IntGreen(15.0f);
+		unsigned int uiBlue1 = (unsigned int)m_frgbaColor1.IntBlue(15.0f);
+
+		unsigned int uiRed2 = (unsigned int)m_frgbaColor2.IntRed(15.0f);
+		unsigned int uiGreen2 = (unsigned int)m_frgbaColor2.IntGreen(15.0f);
+		unsigned int uiBlue2 = (unsigned int)m_frgbaColor2.IntBlue(15.0f);
+
+		m_pencodingbitsRGB8->t.red1a = uiRed1 >> 2;
+		m_pencodingbitsRGB8->t.red1b = uiRed1;
+		m_pencodingbitsRGB8->t.green1 = uiGreen1;
+		m_pencodingbitsRGB8->t.blue1 = uiBlue1;
+
+		m_pencodingbitsRGB8->t.red2 = uiRed2;
+		m_pencodingbitsRGB8->t.green2 = uiGreen2;
+		m_pencodingbitsRGB8->t.blue2 = uiBlue2;
+
+		m_pencodingbitsRGB8->t.da = m_uiCW1 >> 1;
+		m_pencodingbitsRGB8->t.db = m_uiCW1;
+
+		m_pencodingbitsRGB8->t.diff = 1;
+
+		Block4x4Encoding_ETC1::SetEncodingBits_Selectors();
+
+		// create an invalid R differential to trigger T mode
+		m_pencodingbitsRGB8->t.detect1 = 0;
+		m_pencodingbitsRGB8->t.detect2 = 0;
+		int iRed2 = (int)m_pencodingbitsRGB8->differential.red1 + (int)m_pencodingbitsRGB8->differential.dred2;
+		if (iRed2 >= 4)
+		{
+			m_pencodingbitsRGB8->t.detect1 = 7;
+			m_pencodingbitsRGB8->t.detect2 = 0;
+		}
+		else
+		{
+			m_pencodingbitsRGB8->t.detect1 = 0;
+			m_pencodingbitsRGB8->t.detect2 = 1;
+		}
+
+		if (SANITY_CHECK)
+		{
+			iRed2 = (int)m_pencodingbitsRGB8->differential.red1 + (int)m_pencodingbitsRGB8->differential.dred2;
+
+			// make sure red overflows
+			assert(iRed2 < 0 || iRed2 > 31);
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// set the encoding bits based on encoding state for H mode
+	//
+	// colors and selectors may need to swap in order to generate lsb of distance index
+	//
+	void Block4x4Encoding_RGB8::SetEncodingBits_H(void)
+	{
+		static const bool SANITY_CHECK = true;
+
+		assert(m_mode == MODE_H);
+		assert(m_boolDiff == true);
+
+		unsigned int uiRed1 = (unsigned int)m_frgbaColor1.IntRed(15.0f);
+		unsigned int uiGreen1 = (unsigned int)m_frgbaColor1.IntGreen(15.0f);
+		unsigned int uiBlue1 = (unsigned int)m_frgbaColor1.IntBlue(15.0f);
+
+		unsigned int uiRed2 = (unsigned int)m_frgbaColor2.IntRed(15.0f);
+		unsigned int uiGreen2 = (unsigned int)m_frgbaColor2.IntGreen(15.0f);
+		unsigned int uiBlue2 = (unsigned int)m_frgbaColor2.IntBlue(15.0f);
+
+		unsigned int uiColor1 = (uiRed1 << 16) + (uiGreen1 << 8) + uiBlue1;
+		unsigned int uiColor2 = (uiRed2 << 16) + (uiGreen2 << 8) + uiBlue2;
+
+		bool boolOddDistance = m_uiCW1 & 1;
+		bool boolSwapColors = (uiColor1 < uiColor2) ^ !boolOddDistance;
+
+		if (boolSwapColors)
+		{
+			m_pencodingbitsRGB8->h.red1 = uiRed2;
+			m_pencodingbitsRGB8->h.green1a = uiGreen2 >> 1;
+			m_pencodingbitsRGB8->h.green1b = uiGreen2;
+			m_pencodingbitsRGB8->h.blue1a = uiBlue2 >> 3;
+			m_pencodingbitsRGB8->h.blue1b = uiBlue2 >> 1;
+			m_pencodingbitsRGB8->h.blue1c = uiBlue2;
+
+			m_pencodingbitsRGB8->h.red2 = uiRed1;
+			m_pencodingbitsRGB8->h.green2a = uiGreen1 >> 1;
+			m_pencodingbitsRGB8->h.green2b = uiGreen1;
+			m_pencodingbitsRGB8->h.blue2 = uiBlue1;
+
+			m_pencodingbitsRGB8->h.da = m_uiCW1 >> 2;
+			m_pencodingbitsRGB8->h.db = m_uiCW1 >> 1;
+		}
+		else
+		{
+			m_pencodingbitsRGB8->h.red1 = uiRed1;
+			m_pencodingbitsRGB8->h.green1a = uiGreen1 >> 1;
+			m_pencodingbitsRGB8->h.green1b = uiGreen1;
+			m_pencodingbitsRGB8->h.blue1a = uiBlue1 >> 3;
+			m_pencodingbitsRGB8->h.blue1b = uiBlue1 >> 1;
+			m_pencodingbitsRGB8->h.blue1c = uiBlue1;
+
+			m_pencodingbitsRGB8->h.red2 = uiRed2;
+			m_pencodingbitsRGB8->h.green2a = uiGreen2 >> 1;
+			m_pencodingbitsRGB8->h.green2b = uiGreen2;
+			m_pencodingbitsRGB8->h.blue2 = uiBlue2;
+
+			m_pencodingbitsRGB8->h.da = m_uiCW1 >> 2;
+			m_pencodingbitsRGB8->h.db = m_uiCW1 >> 1;
+		}
+
+		m_pencodingbitsRGB8->h.diff = 1;
+
+		Block4x4Encoding_ETC1::SetEncodingBits_Selectors();
+
+		if (boolSwapColors)
+		{
+			m_pencodingbitsRGB8->h.selectors ^= 0x0000FFFF;
+		}
+
+		// create an invalid R differential to trigger T mode
+		m_pencodingbitsRGB8->h.detect1 = 0;
+		m_pencodingbitsRGB8->h.detect2 = 0;
+		m_pencodingbitsRGB8->h.detect3 = 0;
+		int iRed2 = (int)m_pencodingbitsRGB8->differential.red1 + (int)m_pencodingbitsRGB8->differential.dred2;
+		int iGreen2 = (int)m_pencodingbitsRGB8->differential.green1 + (int)m_pencodingbitsRGB8->differential.dgreen2;
+		if (iRed2 < 0 || iRed2 > 31)
+		{
+			m_pencodingbitsRGB8->h.detect1 = 1;
+		}
+		if (iGreen2 >= 4)
+		{
+			m_pencodingbitsRGB8->h.detect2 = 7;
+			m_pencodingbitsRGB8->h.detect3 = 0;
+		}
+		else
+		{
+			m_pencodingbitsRGB8->h.detect2 = 0;
+			m_pencodingbitsRGB8->h.detect3 = 1;
+		}
+
+		if (SANITY_CHECK)
+		{
+			iRed2 = (int)m_pencodingbitsRGB8->differential.red1 + (int)m_pencodingbitsRGB8->differential.dred2;
+			iGreen2 = (int)m_pencodingbitsRGB8->differential.green1 + (int)m_pencodingbitsRGB8->differential.dgreen2;
+
+			// make sure red doesn't overflow and green does
+			assert(iRed2 >= 0 && iRed2 <= 31);
+			assert(iGreen2 < 0 || iGreen2 > 31);
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// set the encoding bits based on encoding state for Planar mode
+	//
+	void Block4x4Encoding_RGB8::SetEncodingBits_Planar(void)
+	{
+		static const bool SANITY_CHECK = true;
+
+		assert(m_mode == MODE_PLANAR);
+		assert(m_boolDiff == true);
+
+		unsigned int uiOriginRed = (unsigned int)m_frgbaColor1.IntRed(63.0f);
+		unsigned int uiOriginGreen = (unsigned int)m_frgbaColor1.IntGreen(127.0f);
+		unsigned int uiOriginBlue = (unsigned int)m_frgbaColor1.IntBlue(63.0f);
+
+		unsigned int uiHorizRed = (unsigned int)m_frgbaColor2.IntRed(63.0f);
+		unsigned int uiHorizGreen = (unsigned int)m_frgbaColor2.IntGreen(127.0f);
+		unsigned int uiHorizBlue = (unsigned int)m_frgbaColor2.IntBlue(63.0f);
+
+		unsigned int uiVertRed = (unsigned int)m_frgbaColor3.IntRed(63.0f);
+		unsigned int uiVertGreen = (unsigned int)m_frgbaColor3.IntGreen(127.0f);
+		unsigned int uiVertBlue = (unsigned int)m_frgbaColor3.IntBlue(63.0f);
+
+		m_pencodingbitsRGB8->planar.originRed = uiOriginRed;
+		m_pencodingbitsRGB8->planar.originGreen1 = uiOriginGreen >> 6;
+		m_pencodingbitsRGB8->planar.originGreen2 = uiOriginGreen;
+		m_pencodingbitsRGB8->planar.originBlue1 = uiOriginBlue >> 5;
+		m_pencodingbitsRGB8->planar.originBlue2 = uiOriginBlue >> 3;
+		m_pencodingbitsRGB8->planar.originBlue3 = uiOriginBlue >> 1;
+		m_pencodingbitsRGB8->planar.originBlue4 = uiOriginBlue;
+
+		m_pencodingbitsRGB8->planar.horizRed1 = uiHorizRed >> 1;
+		m_pencodingbitsRGB8->planar.horizRed2 = uiHorizRed;
+		m_pencodingbitsRGB8->planar.horizGreen = uiHorizGreen;
+		m_pencodingbitsRGB8->planar.horizBlue1 = uiHorizBlue >> 5;
+		m_pencodingbitsRGB8->planar.horizBlue2 = uiHorizBlue;
+
+		m_pencodingbitsRGB8->planar.vertRed1 = uiVertRed >> 3;
+		m_pencodingbitsRGB8->planar.vertRed2 = uiVertRed;
+		m_pencodingbitsRGB8->planar.vertGreen1 = uiVertGreen >> 2;
+		m_pencodingbitsRGB8->planar.vertGreen2 = uiVertGreen;
+		m_pencodingbitsRGB8->planar.vertBlue = uiVertBlue;
+
+		m_pencodingbitsRGB8->planar.diff = 1;
+
+		// create valid RG differentials and an invalid B differential to trigger planar mode
+		m_pencodingbitsRGB8->planar.detect1 = 0;
+		m_pencodingbitsRGB8->planar.detect2 = 0;
+		m_pencodingbitsRGB8->planar.detect3 = 0;
+		m_pencodingbitsRGB8->planar.detect4 = 0;
+		int iRed2 = (int)m_pencodingbitsRGB8->differential.red1 + (int)m_pencodingbitsRGB8->differential.dred2;
+		int iGreen2 = (int)m_pencodingbitsRGB8->differential.green1 + (int)m_pencodingbitsRGB8->differential.dgreen2;
+		int iBlue2 = (int)m_pencodingbitsRGB8->differential.blue1 + (int)m_pencodingbitsRGB8->differential.dblue2;
+		if (iRed2 < 0 || iRed2 > 31)
+		{
+			m_pencodingbitsRGB8->planar.detect1 = 1;
+		}
+		if (iGreen2 < 0 || iGreen2 > 31)
+		{
+			m_pencodingbitsRGB8->planar.detect2 = 1;
+		}
+		if (iBlue2 >= 4)
+		{
+			m_pencodingbitsRGB8->planar.detect3 = 7;
+			m_pencodingbitsRGB8->planar.detect4 = 0;
+		}
+		else
+		{
+			m_pencodingbitsRGB8->planar.detect3 = 0;
+			m_pencodingbitsRGB8->planar.detect4 = 1;
+		}
+
+		if (SANITY_CHECK)
+		{
+			iRed2 = (int)m_pencodingbitsRGB8->differential.red1 + (int)m_pencodingbitsRGB8->differential.dred2;
+			iGreen2 = (int)m_pencodingbitsRGB8->differential.green1 + (int)m_pencodingbitsRGB8->differential.dgreen2;
+			iBlue2 = (int)m_pencodingbitsRGB8->differential.blue1 + (int)m_pencodingbitsRGB8->differential.dblue2;
+
+			// make sure red and green don't overflow and blue does
+			assert(iRed2 >= 0 && iRed2 <= 31);
+			assert(iGreen2 >= 0 && iGreen2 <= 31);
+			assert(iBlue2 < 0 || iBlue2 > 31);
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// set the decoded colors and decoded alpha based on the encoding state for T mode
+	//
+	void Block4x4Encoding_RGB8::DecodePixels_T(void)
+	{
+
+		float fDistance = s_afTHDistanceTable[m_uiCW1];
+		ColorFloatRGBA frgbaDistance(fDistance, fDistance, fDistance, 0.0f);
+
+		for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+		{
+			switch (m_auiSelectors[uiPixel])
+			{
+			case 0:
+				m_afrgbaDecodedColors[uiPixel] = m_frgbaColor1;
+				break;
+
+			case 1:
+				m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor2 + frgbaDistance).ClampRGB();
+				break;
+
+			case 2:
+				m_afrgbaDecodedColors[uiPixel] = m_frgbaColor2;
+				break;
+
+			case 3:
+				m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor2 - frgbaDistance).ClampRGB();
+				break;
+			}
+
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// set the decoded colors and decoded alpha based on the encoding state for H mode
+	//
+	void Block4x4Encoding_RGB8::DecodePixels_H(void)
+	{
+
+		float fDistance = s_afTHDistanceTable[m_uiCW1];
+		ColorFloatRGBA frgbaDistance(fDistance, fDistance, fDistance, 0.0f);
+
+		for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+		{
+			switch (m_auiSelectors[uiPixel])
+			{
+			case 0:
+				m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor1 + frgbaDistance).ClampRGB();
+				break;
+
+			case 1:
+				m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor1 - frgbaDistance).ClampRGB();
+				break;
+
+			case 2:
+				m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor2 + frgbaDistance).ClampRGB();
+				break;
+
+			case 3:
+				m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor2 - frgbaDistance).ClampRGB();
+				break;
+			}
+
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// set the decoded colors and decoded alpha based on the encoding state for Planar mode
+	//
+	void Block4x4Encoding_RGB8::DecodePixels_Planar(void)
+	{
+
+		int iRO = (int)roundf(m_frgbaColor1.fR * 255.0f);
+		int iGO = (int)roundf(m_frgbaColor1.fG * 255.0f);
+		int iBO = (int)roundf(m_frgbaColor1.fB * 255.0f);
+
+		int iRH = (int)roundf(m_frgbaColor2.fR * 255.0f);
+		int iGH = (int)roundf(m_frgbaColor2.fG * 255.0f);
+		int iBH = (int)roundf(m_frgbaColor2.fB * 255.0f);
+
+		int iRV = (int)roundf(m_frgbaColor3.fR * 255.0f);
+		int iGV = (int)roundf(m_frgbaColor3.fG * 255.0f);
+		int iBV = (int)roundf(m_frgbaColor3.fB * 255.0f);
+
+		for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+		{
+			int iX = (int)(uiPixel >> 2);
+			int iY = (int)(uiPixel & 3);
+
+			int iR = (iX*(iRH - iRO) + iY*(iRV - iRO) + 4*iRO + 2) >> 2;
+			int iG = (iX*(iGH - iGO) + iY*(iGV - iGO) + 4*iGO + 2) >> 2;
+			int iB = (iX*(iBH - iBO) + iY*(iBV - iBO) + 4*iBO + 2) >> 2;
+
+			ColorFloatRGBA frgba;
+			frgba.fR = (float)iR / 255.0f;
+			frgba.fG = (float)iG / 255.0f;
+			frgba.fB = (float)iB / 255.0f;
+			frgba.fA = 1.0f;
+
+			m_afrgbaDecodedColors[uiPixel] = frgba.ClampRGB();
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// perform a linear regression for the a_uiPixels in a_pafrgbaPixels[]
+	//
+	// output the closest color line using a_pfrgbaSlope and a_pfrgbaOffset
+	//
+	void Block4x4Encoding_RGB8::ColorRegression(ColorFloatRGBA *a_pafrgbaPixels, unsigned int a_uiPixels,
+												ColorFloatRGBA *a_pfrgbaSlope, ColorFloatRGBA *a_pfrgbaOffset)
+	{
+		typedef struct
+		{
+			float f[4];
+		} Float4;
+
+		Float4 *paf4Pixels = (Float4 *)(a_pafrgbaPixels);
+		Float4 *pf4Slope = (Float4 *)(a_pfrgbaSlope);
+		Float4 *pf4Offset = (Float4 *)(a_pfrgbaOffset);
+
+		float afX[MAX_PLANAR_REGRESSION_SIZE];
+		float afY[MAX_PLANAR_REGRESSION_SIZE];
+
+		// handle r, g and b separately.  don't bother with a
+		for (unsigned int uiComponent = 0; uiComponent < 3; uiComponent++)
+		{
+			for (unsigned int uiPixel = 0; uiPixel < a_uiPixels; uiPixel++)
+			{
+				afX[uiPixel] = (float)uiPixel;
+				afY[uiPixel] = paf4Pixels[uiPixel].f[uiComponent];
+				
+			}
+			Etc::Regression(afX, afY, a_uiPixels,
+				&(pf4Slope->f[uiComponent]), &(pf4Offset->f[uiComponent]));
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+}

+ 96 - 0
thirdparty/etc2comp/EtcBlock4x4Encoding_RGB8.h

@@ -0,0 +1,96 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "EtcBlock4x4Encoding_ETC1.h"
+
+namespace Etc
+{
+
+	class Block4x4Encoding_RGB8 : public Block4x4Encoding_ETC1
+	{
+	public:
+
+		Block4x4Encoding_RGB8(void);
+		virtual ~Block4x4Encoding_RGB8(void);
+
+		virtual void InitFromEncodingBits(Block4x4 *a_pblockParent,
+											unsigned char *a_paucEncodingBits,
+											ColorFloatRGBA *a_pafrgbaSource,
+
+											ErrorMetric a_errormetric);
+
+		virtual void PerformIteration(float a_fEffort);
+		
+		virtual void SetEncodingBits(void);
+
+		inline ColorFloatRGBA GetColor3(void) const
+		{
+			return m_frgbaColor3;
+		}
+
+	protected:
+
+		static const unsigned int PLANAR_CORNER_COLORS = 3;
+		static const unsigned int MAX_PLANAR_REGRESSION_SIZE = 4;
+		static const unsigned int TH_DISTANCES = 8;
+
+		static float s_afTHDistanceTable[TH_DISTANCES];
+
+		void TryPlanar(unsigned int a_uiRadius);
+		void TryTAndH(unsigned int a_uiRadius);
+
+		void InitFromEncodingBits_Planar(void);
+
+		ColorFloatRGBA	m_frgbaColor3;		// used for planar
+
+		void SetEncodingBits_T(void);
+		void SetEncodingBits_H(void);
+		void SetEncodingBits_Planar(void);
+
+		// state shared between iterations
+		ColorFloatRGBA	m_frgbaOriginalColor1_TAndH;
+		ColorFloatRGBA	m_frgbaOriginalColor2_TAndH;
+
+		void CalculateBaseColorsForTAndH(void);
+		void TryT(unsigned int a_uiRadius);
+		void TryT_BestSelectorCombination(void);
+		void TryH(unsigned int a_uiRadius);
+		void TryH_BestSelectorCombination(void);
+
+	private:
+
+		void InitFromEncodingBits_T(void);
+		void InitFromEncodingBits_H(void);
+
+		void CalculatePlanarCornerColors(void);
+
+		void ColorRegression(ColorFloatRGBA *a_pafrgbaPixels, unsigned int a_uiPixels,
+			ColorFloatRGBA *a_pfrgbaSlope, ColorFloatRGBA *a_pfrgbaOffset);
+
+		bool TwiddlePlanar(void);
+		bool TwiddlePlanarR();
+		bool TwiddlePlanarG();
+		bool TwiddlePlanarB();
+
+		void DecodePixels_T(void);
+		void DecodePixels_H(void);
+		void DecodePixels_Planar(void);
+
+	};
+
+} // namespace Etc

+ 1819 - 0
thirdparty/etc2comp/EtcBlock4x4Encoding_RGB8A1.cpp

@@ -0,0 +1,1819 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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.
+ */
+
+/*
+EtcBlock4x4Encoding_RGB8A1.cpp contains:
+	Block4x4Encoding_RGB8A1
+	Block4x4Encoding_RGB8A1_Opaque
+	Block4x4Encoding_RGB8A1_Transparent
+
+These encoders are used when targetting file format RGB8A1.
+
+Block4x4Encoding_RGB8A1_Opaque is used when all pixels in the 4x4 block are opaque
+Block4x4Encoding_RGB8A1_Transparent is used when all pixels in the 4x4 block are transparent
+Block4x4Encoding_RGB8A1 is used when there is a mixture of alphas in the 4x4 block
+
+*/
+
+#include "EtcConfig.h"
+#include "EtcBlock4x4Encoding_RGB8A1.h"
+
+#include "EtcBlock4x4.h"
+#include "EtcBlock4x4EncodingBits.h"
+#include "EtcBlock4x4Encoding_RGB8.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+namespace Etc
+{
+	
+	// ####################################################################################################
+	// Block4x4Encoding_RGB8A1
+	// ####################################################################################################
+
+	float Block4x4Encoding_RGB8A1::s_aafCwOpaqueUnsetTable[CW_RANGES][SELECTORS] =
+	{
+		{ 0.0f / 255.0f, 8.0f / 255.0f, 0.0f / 255.0f, -8.0f / 255.0f },
+		{ 0.0f / 255.0f, 17.0f / 255.0f, 0.0f / 255.0f, -17.0f / 255.0f },
+		{ 0.0f / 255.0f, 29.0f / 255.0f, 0.0f / 255.0f, -29.0f / 255.0f },
+		{ 0.0f / 255.0f, 42.0f / 255.0f, 0.0f / 255.0f, -42.0f / 255.0f },
+		{ 0.0f / 255.0f, 60.0f / 255.0f, 0.0f / 255.0f, -60.0f / 255.0f },
+		{ 0.0f / 255.0f, 80.0f / 255.0f, 0.0f / 255.0f, -80.0f / 255.0f },
+		{ 0.0f / 255.0f, 106.0f / 255.0f, 0.0f / 255.0f, -106.0f / 255.0f },
+		{ 0.0f / 255.0f, 183.0f / 255.0f, 0.0f / 255.0f, -183.0f / 255.0f }
+	};
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+	Block4x4Encoding_RGB8A1::Block4x4Encoding_RGB8A1(void)
+	{
+		m_pencodingbitsRGB8 = nullptr;
+		m_boolOpaque = false;
+		m_boolTransparent = false;
+		m_boolPunchThroughPixels = true;
+
+	}
+	Block4x4Encoding_RGB8A1::~Block4x4Encoding_RGB8A1(void) {}
+	// ----------------------------------------------------------------------------------------------------
+	// initialization prior to encoding
+	// a_pblockParent points to the block associated with this encoding
+	// a_errormetric is used to choose the best encoding
+	// a_pafrgbaSource points to a 4x4 block subset of the source image
+	// a_paucEncodingBits points to the final encoding bits
+	//
+	void Block4x4Encoding_RGB8A1::InitFromSource(Block4x4 *a_pblockParent,
+													ColorFloatRGBA *a_pafrgbaSource,
+													unsigned char *a_paucEncodingBits,
+													ErrorMetric a_errormetric)
+	{
+
+		Block4x4Encoding_RGB8::InitFromSource(a_pblockParent,
+			a_pafrgbaSource,
+			a_paucEncodingBits,
+			a_errormetric);
+
+		m_boolOpaque = a_pblockParent->GetSourceAlphaMix() == Block4x4::SourceAlphaMix::OPAQUE;
+		m_boolTransparent = a_pblockParent->GetSourceAlphaMix() == Block4x4::SourceAlphaMix::TRANSPARENT;
+		m_boolPunchThroughPixels = a_pblockParent->HasPunchThroughPixels();
+
+		for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+		{
+			if (m_pafrgbaSource[uiPixel].fA >= 0.5f)
+			{
+				m_afDecodedAlphas[uiPixel] = 1.0f;
+			}
+			else
+			{
+				m_afDecodedAlphas[uiPixel] = 0.0f;
+			}
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// initialization from the encoding bits of a previous encoding
+	// a_pblockParent points to the block associated with this encoding
+	// a_errormetric is used to choose the best encoding
+	// a_pafrgbaSource points to a 4x4 block subset of the source image
+	// a_paucEncodingBits points to the final encoding bits of a previous encoding
+	//
+	void Block4x4Encoding_RGB8A1::InitFromEncodingBits(Block4x4 *a_pblockParent,
+														unsigned char *a_paucEncodingBits,
+														ColorFloatRGBA *a_pafrgbaSource,
+														ErrorMetric a_errormetric)
+	{
+
+
+		InitFromEncodingBits_ETC1(a_pblockParent,
+			a_paucEncodingBits,
+			a_pafrgbaSource,
+			a_errormetric);
+
+		m_pencodingbitsRGB8 = (Block4x4EncodingBits_RGB8 *)a_paucEncodingBits;
+
+		// detect if there is a T, H or Planar mode present
+		int iRed1 = m_pencodingbitsRGB8->differential.red1;
+		int iDRed2 = m_pencodingbitsRGB8->differential.dred2;
+		int iRed2 = iRed1 + iDRed2;
+
+		int iGreen1 = m_pencodingbitsRGB8->differential.green1;
+		int iDGreen2 = m_pencodingbitsRGB8->differential.dgreen2;
+		int iGreen2 = iGreen1 + iDGreen2;
+
+		int iBlue1 = m_pencodingbitsRGB8->differential.blue1;
+		int iDBlue2 = m_pencodingbitsRGB8->differential.dblue2;
+		int iBlue2 = iBlue1 + iDBlue2;
+
+		if (iRed2 < 0 || iRed2 > 31)
+		{
+			InitFromEncodingBits_T();
+		}
+		else if (iGreen2 < 0 || iGreen2 > 31)
+		{
+			InitFromEncodingBits_H();
+		}
+		else if (iBlue2 < 0 || iBlue2 > 31)
+		{
+			Block4x4Encoding_RGB8::InitFromEncodingBits_Planar();
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// initialization from the encoding bits of a previous encoding assuming the encoding is an ETC1 mode.
+	// if it isn't an ETC1 mode, this will be overwritten later
+	//
+	void Block4x4Encoding_RGB8A1::InitFromEncodingBits_ETC1(Block4x4 *a_pblockParent,
+		unsigned char *a_paucEncodingBits,
+		ColorFloatRGBA *a_pafrgbaSource,
+		ErrorMetric a_errormetric)
+	{
+		Block4x4Encoding::Init(a_pblockParent, a_pafrgbaSource,
+			a_errormetric);
+
+		m_pencodingbitsRGB8 = (Block4x4EncodingBits_RGB8 *)a_paucEncodingBits;
+
+		m_mode = MODE_ETC1;
+		m_boolDiff = true;
+		m_boolFlip = m_pencodingbitsRGB8->differential.flip;
+		m_boolOpaque = m_pencodingbitsRGB8->differential.diff;
+
+		int iR2 = m_pencodingbitsRGB8->differential.red1 + m_pencodingbitsRGB8->differential.dred2;
+		if (iR2 < 0)
+		{
+			iR2 = 0;
+		}
+		else if (iR2 > 31)
+		{
+			iR2 = 31;
+		}
+
+		int iG2 = m_pencodingbitsRGB8->differential.green1 + m_pencodingbitsRGB8->differential.dgreen2;
+		if (iG2 < 0)
+		{
+			iG2 = 0;
+		}
+		else if (iG2 > 31)
+		{
+			iG2 = 31;
+		}
+
+		int iB2 = m_pencodingbitsRGB8->differential.blue1 + m_pencodingbitsRGB8->differential.dblue2;
+		if (iB2 < 0)
+		{
+			iB2 = 0;
+		}
+		else if (iB2 > 31)
+		{
+			iB2 = 31;
+		}
+
+		m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB5(m_pencodingbitsRGB8->differential.red1, m_pencodingbitsRGB8->differential.green1, m_pencodingbitsRGB8->differential.blue1);
+		m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB5((unsigned char)iR2, (unsigned char)iG2, (unsigned char)iB2);
+
+		m_uiCW1 = m_pencodingbitsRGB8->differential.cw1;
+		m_uiCW2 = m_pencodingbitsRGB8->differential.cw2;
+
+		Block4x4Encoding_ETC1::InitFromEncodingBits_Selectors();
+
+		Decode_ETC1();
+
+		CalcBlockError();
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// initialization from the encoding bits of a previous encoding if T mode is detected
+	//
+	void Block4x4Encoding_RGB8A1::InitFromEncodingBits_T(void)
+	{
+		m_mode = MODE_T;
+
+		unsigned char ucRed1 = (unsigned char)((m_pencodingbitsRGB8->t.red1a << 2) +
+								m_pencodingbitsRGB8->t.red1b);
+		unsigned char ucGreen1 = m_pencodingbitsRGB8->t.green1;
+		unsigned char ucBlue1 = m_pencodingbitsRGB8->t.blue1;
+
+		unsigned char ucRed2 = m_pencodingbitsRGB8->t.red2;
+		unsigned char ucGreen2 = m_pencodingbitsRGB8->t.green2;
+		unsigned char ucBlue2 = m_pencodingbitsRGB8->t.blue2;
+
+		m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4(ucRed1, ucGreen1, ucBlue1);
+		m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4(ucRed2, ucGreen2, ucBlue2);
+
+		m_uiCW1 = (m_pencodingbitsRGB8->t.da << 1) + m_pencodingbitsRGB8->t.db;
+
+		Block4x4Encoding_ETC1::InitFromEncodingBits_Selectors();
+
+		DecodePixels_T();
+
+		CalcBlockError();
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// initialization from the encoding bits of a previous encoding if H mode is detected
+	//
+	void Block4x4Encoding_RGB8A1::InitFromEncodingBits_H(void)
+	{
+		m_mode = MODE_H;
+
+		unsigned char ucRed1 = m_pencodingbitsRGB8->h.red1;
+		unsigned char ucGreen1 = (unsigned char)((m_pencodingbitsRGB8->h.green1a << 1) +
+									m_pencodingbitsRGB8->h.green1b);
+		unsigned char ucBlue1 = (unsigned char)((m_pencodingbitsRGB8->h.blue1a << 3) +
+								(m_pencodingbitsRGB8->h.blue1b << 1) +
+								m_pencodingbitsRGB8->h.blue1c);
+
+		unsigned char ucRed2 = m_pencodingbitsRGB8->h.red2;
+		unsigned char ucGreen2 = (unsigned char)((m_pencodingbitsRGB8->h.green2a << 1) +
+									m_pencodingbitsRGB8->h.green2b);
+		unsigned char ucBlue2 = m_pencodingbitsRGB8->h.blue2;
+
+		m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4(ucRed1, ucGreen1, ucBlue1);
+		m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4(ucRed2, ucGreen2, ucBlue2);
+
+		// used to determine the LSB of the CW
+		unsigned int uiRGB1 = (unsigned int)(((int)ucRed1 << 16) + ((int)ucGreen1 << 8) + (int)ucBlue1);
+		unsigned int uiRGB2 = (unsigned int)(((int)ucRed2 << 16) + ((int)ucGreen2 << 8) + (int)ucBlue2);
+
+		m_uiCW1 = (m_pencodingbitsRGB8->h.da << 2) + (m_pencodingbitsRGB8->h.db << 1);
+		if (uiRGB1 >= uiRGB2)
+		{
+			m_uiCW1++;
+		}
+
+		Block4x4Encoding_ETC1::InitFromEncodingBits_Selectors();
+
+		DecodePixels_H();
+
+		CalcBlockError();
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// for ETC1 modes, set the decoded colors and decoded alpha based on the encoding state
+	//
+	void Block4x4Encoding_RGB8A1::Decode_ETC1(void)
+	{
+
+		const unsigned int *pauiPixelOrder = m_boolFlip ? s_auiPixelOrderFlip1 : s_auiPixelOrderFlip0;
+
+		for (unsigned int uiPixelOrder = 0; uiPixelOrder < PIXELS; uiPixelOrder++)
+		{
+			ColorFloatRGBA *pfrgbaCenter = uiPixelOrder < 8 ? &m_frgbaColor1 : &m_frgbaColor2;
+			unsigned int uiCW = uiPixelOrder < 8 ? m_uiCW1 : m_uiCW2;
+
+			unsigned int uiPixel = pauiPixelOrder[uiPixelOrder];
+
+			float fDelta;
+			if (m_boolOpaque)
+				fDelta = Block4x4Encoding_ETC1::s_aafCwTable[uiCW][m_auiSelectors[uiPixel]];
+			else 
+				fDelta = s_aafCwOpaqueUnsetTable[uiCW][m_auiSelectors[uiPixel]];
+
+			if (m_boolOpaque == false && m_auiSelectors[uiPixel] == TRANSPARENT_SELECTOR)
+			{
+				m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA();
+				m_afDecodedAlphas[uiPixel] = 0.0f;
+			}
+			else
+			{
+				m_afrgbaDecodedColors[uiPixel] = (*pfrgbaCenter + fDelta).ClampRGB();
+				m_afDecodedAlphas[uiPixel] = 1.0f;
+			}
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// for T mode, set the decoded colors and decoded alpha based on the encoding state
+	//
+	void Block4x4Encoding_RGB8A1::DecodePixels_T(void)
+	{
+
+		float fDistance = s_afTHDistanceTable[m_uiCW1];
+		ColorFloatRGBA frgbaDistance(fDistance, fDistance, fDistance, 0.0f);
+
+		for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+		{
+			switch (m_auiSelectors[uiPixel])
+			{
+			case 0:
+				m_afrgbaDecodedColors[uiPixel] = m_frgbaColor1;
+				m_afDecodedAlphas[uiPixel] = 1.0f;
+				break;
+
+			case 1:
+				m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor2 + frgbaDistance).ClampRGB();
+				m_afDecodedAlphas[uiPixel] = 1.0f;
+				break;
+
+			case 2:
+				if (m_boolOpaque == false)
+				{
+					m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA();
+					m_afDecodedAlphas[uiPixel] = 0.0f;
+				}
+				else
+				{
+					m_afrgbaDecodedColors[uiPixel] = m_frgbaColor2;
+					m_afDecodedAlphas[uiPixel] = 1.0f;
+				}
+				break;
+
+			case 3:
+				m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor2 - frgbaDistance).ClampRGB();
+				m_afDecodedAlphas[uiPixel] = 1.0f;
+				break;
+			}
+
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// for H mode, set the decoded colors and decoded alpha based on the encoding state
+	//
+	void Block4x4Encoding_RGB8A1::DecodePixels_H(void)
+	{
+
+		float fDistance = s_afTHDistanceTable[m_uiCW1];
+		ColorFloatRGBA frgbaDistance(fDistance, fDistance, fDistance, 0.0f);
+
+		for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+		{
+			switch (m_auiSelectors[uiPixel])
+			{
+			case 0:
+				m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor1 + frgbaDistance).ClampRGB();
+				m_afDecodedAlphas[uiPixel] = 1.0f;
+				break;
+
+			case 1:
+				m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor1 - frgbaDistance).ClampRGB();
+				m_afDecodedAlphas[uiPixel] = 1.0f;
+				break;
+
+			case 2:
+				if (m_boolOpaque == false)
+				{
+					m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA();
+					m_afDecodedAlphas[uiPixel] = 0.0f;
+				}
+				else
+				{
+					m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor2 + frgbaDistance).ClampRGB();
+					m_afDecodedAlphas[uiPixel] = 1.0f;
+				}
+				break;
+
+			case 3:
+				m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor2 - frgbaDistance).ClampRGB();
+				m_afDecodedAlphas[uiPixel] = 1.0f;
+				break;
+			}
+
+		}
+
+	}
+
+
+	// ----------------------------------------------------------------------------------------------------
+	// perform a single encoding iteration
+	// replace the encoding if a better encoding was found
+	// subsequent iterations generally take longer for each iteration
+	// set m_boolDone if encoding is perfect or encoding is finished based on a_fEffort
+	//
+	// RGB8A1 can't use individual mode
+	// RGB8A1 with transparent pixels can't use planar mode
+	//
+	void Block4x4Encoding_RGB8A1::PerformIteration(float a_fEffort)
+	{
+		assert(!m_boolOpaque);
+		assert(!m_boolTransparent);
+		assert(!m_boolDone);
+
+		switch (m_uiEncodingIterations)
+		{
+		case 0:
+			PerformFirstIteration();
+			break;
+
+		case 1:
+			TryDifferential(m_boolMostLikelyFlip, 1, 0, 0);
+			break;
+
+		case 2:
+			TryDifferential(!m_boolMostLikelyFlip, 1, 0, 0);
+			if (a_fEffort <= 39.5f)
+			{
+				m_boolDone = true;
+			}
+			break;
+
+		case 3:
+			Block4x4Encoding_RGB8::CalculateBaseColorsForTAndH();
+			TryT(1);
+			TryH(1);
+			if (a_fEffort <= 49.5f)
+			{
+				m_boolDone = true;
+			}
+			break;
+
+		case 4:
+			TryDegenerates1();
+			if (a_fEffort <= 59.5f)
+			{
+				m_boolDone = true;
+			}
+			break;
+
+		case 5:
+			TryDegenerates2();
+			if (a_fEffort <= 69.5f)
+			{
+				m_boolDone = true;
+			}
+			break;
+
+		case 6:
+			TryDegenerates3();
+			if (a_fEffort <= 79.5f)
+			{
+				m_boolDone = true;
+			}
+			break;
+
+		case 7:
+			TryDegenerates4();
+			m_boolDone = true;
+			break;
+
+		default:
+			assert(0);
+			break;
+		}
+
+		m_uiEncodingIterations++;
+
+		SetDoneIfPerfect();
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// find best initial encoding to ensure block has a valid encoding
+	//
+	void Block4x4Encoding_RGB8A1::PerformFirstIteration(void)
+	{
+		Block4x4Encoding_ETC1::CalculateMostLikelyFlip();
+
+		m_fError = FLT_MAX;
+
+		TryDifferential(m_boolMostLikelyFlip, 0, 0, 0);
+		SetDoneIfPerfect();
+		if (m_boolDone)
+		{
+			return;
+		}
+		TryDifferential(!m_boolMostLikelyFlip, 0, 0, 0);
+		SetDoneIfPerfect();
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// mostly copied from ETC1
+	// differences:
+	//		Block4x4Encoding_RGB8A1 encodingTry = *this;
+	//
+	void Block4x4Encoding_RGB8A1::TryDifferential(bool a_boolFlip, unsigned int a_uiRadius, 
+													int a_iGrayOffset1, int a_iGrayOffset2)
+	{
+
+		ColorFloatRGBA frgbaColor1;
+		ColorFloatRGBA frgbaColor2;
+
+		const unsigned int *pauiPixelMapping1;
+		const unsigned int *pauiPixelMapping2;
+
+		if (a_boolFlip)
+		{
+			frgbaColor1 = m_frgbaSourceAverageTop;
+			frgbaColor2 = m_frgbaSourceAverageBottom;
+
+			pauiPixelMapping1 = s_auiTopPixelMapping;
+			pauiPixelMapping2 = s_auiBottomPixelMapping;
+		}
+		else
+		{
+			frgbaColor1 = m_frgbaSourceAverageLeft;
+			frgbaColor2 = m_frgbaSourceAverageRight;
+
+			pauiPixelMapping1 = s_auiLeftPixelMapping;
+			pauiPixelMapping2 = s_auiRightPixelMapping;
+		}
+
+		DifferentialTrys trys(frgbaColor1, frgbaColor2, pauiPixelMapping1, pauiPixelMapping2, 
+								a_uiRadius, a_iGrayOffset1, a_iGrayOffset2);
+
+		Block4x4Encoding_RGB8A1 encodingTry = *this;
+		encodingTry.m_boolFlip = a_boolFlip;
+
+		encodingTry.TryDifferentialHalf(&trys.m_half1);
+		encodingTry.TryDifferentialHalf(&trys.m_half2);
+
+		// find best halves that are within differential range
+		DifferentialTrys::Try *ptryBest1 = nullptr;
+		DifferentialTrys::Try *ptryBest2 = nullptr;
+		encodingTry.m_fError = FLT_MAX;
+
+		// see if the best of each half are in differential range
+		int iDRed = trys.m_half2.m_ptryBest->m_iRed - trys.m_half1.m_ptryBest->m_iRed;
+		int iDGreen = trys.m_half2.m_ptryBest->m_iGreen - trys.m_half1.m_ptryBest->m_iGreen;
+		int iDBlue = trys.m_half2.m_ptryBest->m_iBlue - trys.m_half1.m_ptryBest->m_iBlue;
+		if (iDRed >= -4 && iDRed <= 3 && iDGreen >= -4 && iDGreen <= 3 && iDBlue >= -4 && iDBlue <= 3)
+		{
+			ptryBest1 = trys.m_half1.m_ptryBest;
+			ptryBest2 = trys.m_half2.m_ptryBest;
+			encodingTry.m_fError = trys.m_half1.m_ptryBest->m_fError + trys.m_half2.m_ptryBest->m_fError;
+		}
+		else
+		{
+			// else, find the next best halves that are in differential range
+			for (DifferentialTrys::Try *ptry1 = &trys.m_half1.m_atry[0];
+			ptry1 < &trys.m_half1.m_atry[trys.m_half1.m_uiTrys];
+				ptry1++)
+			{
+				for (DifferentialTrys::Try *ptry2 = &trys.m_half2.m_atry[0];
+				ptry2 < &trys.m_half2.m_atry[trys.m_half2.m_uiTrys];
+					ptry2++)
+				{
+					iDRed = ptry2->m_iRed - ptry1->m_iRed;
+					bool boolValidRedDelta = iDRed <= 3 && iDRed >= -4;
+					iDGreen = ptry2->m_iGreen - ptry1->m_iGreen;
+					bool boolValidGreenDelta = iDGreen <= 3 && iDGreen >= -4;
+					iDBlue = ptry2->m_iBlue - ptry1->m_iBlue;
+					bool boolValidBlueDelta = iDBlue <= 3 && iDBlue >= -4;
+
+					if (boolValidRedDelta && boolValidGreenDelta && boolValidBlueDelta)
+					{
+						float fError = ptry1->m_fError + ptry2->m_fError;
+
+						if (fError < encodingTry.m_fError)
+						{
+							encodingTry.m_fError = fError;
+
+							ptryBest1 = ptry1;
+							ptryBest2 = ptry2;
+						}
+					}
+
+				}
+			}
+			assert(encodingTry.m_fError < FLT_MAX);
+			assert(ptryBest1 != nullptr);
+			assert(ptryBest2 != nullptr);
+		}
+
+		if (encodingTry.m_fError < m_fError)
+		{
+			m_mode = MODE_ETC1;
+			m_boolDiff = true;
+			m_boolFlip = encodingTry.m_boolFlip;
+			m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB5((unsigned char)ptryBest1->m_iRed, (unsigned char)ptryBest1->m_iGreen, (unsigned char)ptryBest1->m_iBlue);
+			m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB5((unsigned char)ptryBest2->m_iRed, (unsigned char)ptryBest2->m_iGreen, (unsigned char)ptryBest2->m_iBlue);
+			m_uiCW1 = ptryBest1->m_uiCW;
+			m_uiCW2 = ptryBest2->m_uiCW;
+
+			m_fError = 0.0f;
+			for (unsigned int uiPixelOrder = 0; uiPixelOrder < PIXELS / 2; uiPixelOrder++)
+			{
+				unsigned int uiPixel1 = pauiPixelMapping1[uiPixelOrder];
+				unsigned int uiPixel2 = pauiPixelMapping2[uiPixelOrder];
+
+				unsigned int uiSelector1 = ptryBest1->m_auiSelectors[uiPixelOrder];
+				unsigned int uiSelector2 = ptryBest2->m_auiSelectors[uiPixelOrder];
+
+				m_auiSelectors[uiPixel1] = uiSelector1;
+				m_auiSelectors[uiPixel2] = ptryBest2->m_auiSelectors[uiPixelOrder];
+
+				if (uiSelector1 == TRANSPARENT_SELECTOR)
+				{
+					m_afrgbaDecodedColors[uiPixel1] = ColorFloatRGBA();
+					m_afDecodedAlphas[uiPixel1] = 0.0f;
+				}
+				else
+				{
+					float fDeltaRGB1 = s_aafCwOpaqueUnsetTable[m_uiCW1][uiSelector1];
+					m_afrgbaDecodedColors[uiPixel1] = (m_frgbaColor1 + fDeltaRGB1).ClampRGB();
+					m_afDecodedAlphas[uiPixel1] = 1.0f;
+				}
+
+				if (uiSelector2 == TRANSPARENT_SELECTOR)
+				{
+					m_afrgbaDecodedColors[uiPixel2] = ColorFloatRGBA();
+					m_afDecodedAlphas[uiPixel2] = 0.0f;
+				}
+				else
+				{
+					float fDeltaRGB2 = s_aafCwOpaqueUnsetTable[m_uiCW2][uiSelector2];
+					m_afrgbaDecodedColors[uiPixel2] = (m_frgbaColor2 + fDeltaRGB2).ClampRGB();
+					m_afDecodedAlphas[uiPixel2] = 1.0f;
+				}
+
+				float fDeltaA1 = m_afDecodedAlphas[uiPixel1] - m_pafrgbaSource[uiPixel1].fA;
+				m_fError += fDeltaA1 * fDeltaA1;
+				float fDeltaA2 = m_afDecodedAlphas[uiPixel2] - m_pafrgbaSource[uiPixel2].fA;
+				m_fError += fDeltaA2 * fDeltaA2;
+			}
+
+			m_fError1 = ptryBest1->m_fError;
+			m_fError2 = ptryBest2->m_fError;
+			m_boolSeverelyBentDifferentialColors = trys.m_boolSeverelyBentColors;
+			m_fError = m_fError1 + m_fError2;
+
+			// sanity check
+			{
+				int iRed1 = m_frgbaColor1.IntRed(31.0f);
+				int iGreen1 = m_frgbaColor1.IntGreen(31.0f);
+				int iBlue1 = m_frgbaColor1.IntBlue(31.0f);
+
+				int iRed2 = m_frgbaColor2.IntRed(31.0f);
+				int iGreen2 = m_frgbaColor2.IntGreen(31.0f);
+				int iBlue2 = m_frgbaColor2.IntBlue(31.0f);
+
+				iDRed = iRed2 - iRed1;
+				iDGreen = iGreen2 - iGreen1;
+				iDBlue = iBlue2 - iBlue1;
+
+				assert(iDRed >= -4 && iDRed < 4);
+				assert(iDGreen >= -4 && iDGreen < 4);
+				assert(iDBlue >= -4 && iDBlue < 4);
+			}
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// mostly copied from ETC1
+	// differences:
+	//		uses s_aafCwOpaqueUnsetTable
+	//		color for selector set to 0,0,0,0
+	//
+	void Block4x4Encoding_RGB8A1::TryDifferentialHalf(DifferentialTrys::Half *a_phalf)
+	{
+
+		a_phalf->m_ptryBest = nullptr;
+		float fBestTryError = FLT_MAX;
+
+		a_phalf->m_uiTrys = 0;
+		for (int iRed = a_phalf->m_iRed - (int)a_phalf->m_uiRadius;
+		iRed <= a_phalf->m_iRed + (int)a_phalf->m_uiRadius;
+			iRed++)
+		{
+			assert(iRed >= 0 && iRed <= 31);
+
+			for (int iGreen = a_phalf->m_iGreen - (int)a_phalf->m_uiRadius;
+			iGreen <= a_phalf->m_iGreen + (int)a_phalf->m_uiRadius;
+				iGreen++)
+			{
+				assert(iGreen >= 0 && iGreen <= 31);
+
+				for (int iBlue = a_phalf->m_iBlue - (int)a_phalf->m_uiRadius;
+				iBlue <= a_phalf->m_iBlue + (int)a_phalf->m_uiRadius;
+					iBlue++)
+				{
+					assert(iBlue >= 0 && iBlue <= 31);
+
+					DifferentialTrys::Try *ptry = &a_phalf->m_atry[a_phalf->m_uiTrys];
+					assert(ptry < &a_phalf->m_atry[DifferentialTrys::Half::MAX_TRYS]);
+
+					ptry->m_iRed = iRed;
+					ptry->m_iGreen = iGreen;
+					ptry->m_iBlue = iBlue;
+					ptry->m_fError = FLT_MAX;
+					ColorFloatRGBA frgbaColor = ColorFloatRGBA::ConvertFromRGB5((unsigned char)iRed, (unsigned char)iGreen, (unsigned char)iBlue);
+
+					// try each CW
+					for (unsigned int uiCW = 0; uiCW < CW_RANGES; uiCW++)
+					{
+						unsigned int auiPixelSelectors[PIXELS / 2];
+						ColorFloatRGBA	afrgbaDecodedColors[PIXELS / 2];
+						float afPixelErrors[PIXELS / 2] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX,
+							FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX };
+
+						// pre-compute decoded pixels for each selector
+						ColorFloatRGBA afrgbaSelectors[SELECTORS];
+						assert(SELECTORS == 4);
+						afrgbaSelectors[0] = (frgbaColor + s_aafCwOpaqueUnsetTable[uiCW][0]).ClampRGB();
+						afrgbaSelectors[1] = (frgbaColor + s_aafCwOpaqueUnsetTable[uiCW][1]).ClampRGB();
+						afrgbaSelectors[2] = ColorFloatRGBA();
+						afrgbaSelectors[3] = (frgbaColor + s_aafCwOpaqueUnsetTable[uiCW][3]).ClampRGB();
+
+						for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++)
+						{
+							ColorFloatRGBA *pfrgbaSourcePixel = &m_pafrgbaSource[a_phalf->m_pauiPixelMapping[uiPixel]];
+							ColorFloatRGBA frgbaDecodedPixel;
+
+							for (unsigned int uiSelector = 0; uiSelector < SELECTORS; uiSelector++)
+							{
+								if (pfrgbaSourcePixel->fA < 0.5f)
+								{
+									uiSelector = TRANSPARENT_SELECTOR;
+								}
+								else if (uiSelector == TRANSPARENT_SELECTOR)
+								{
+									continue;
+								}
+
+								frgbaDecodedPixel = afrgbaSelectors[uiSelector];
+
+								float fPixelError;
+								
+								fPixelError = CalcPixelError(frgbaDecodedPixel, m_afDecodedAlphas[a_phalf->m_pauiPixelMapping[uiPixel]],
+																	*pfrgbaSourcePixel);
+
+								if (fPixelError < afPixelErrors[uiPixel])
+								{
+									auiPixelSelectors[uiPixel] = uiSelector;
+									afrgbaDecodedColors[uiPixel] = frgbaDecodedPixel;
+									afPixelErrors[uiPixel] = fPixelError;
+								}
+
+								if (uiSelector == TRANSPARENT_SELECTOR)
+								{
+									break;
+								}
+							}
+						}
+
+						// add up all pixel errors
+						float fCWError = 0.0f;
+						for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++)
+						{
+							fCWError += afPixelErrors[uiPixel];
+						}
+
+						// if best CW so far
+						if (fCWError < ptry->m_fError)
+						{
+							ptry->m_uiCW = uiCW;
+							for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++)
+							{
+								ptry->m_auiSelectors[uiPixel] = auiPixelSelectors[uiPixel];
+							}
+							ptry->m_fError = fCWError;
+						}
+
+					}
+
+					if (ptry->m_fError < fBestTryError)
+					{
+						a_phalf->m_ptryBest = ptry;
+						fBestTryError = ptry->m_fError;
+					}
+
+					assert(ptry->m_fError < FLT_MAX);
+
+					a_phalf->m_uiTrys++;
+				}
+			}
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// try encoding in T mode
+	// save this encoding if it improves the error
+	//
+	// since pixels that use base color1 don't use the distance table, color1 and color2 can be twiddled independently
+	// better encoding can be found if TWIDDLE_RADIUS is set to 2, but it will be much slower
+	//
+	void Block4x4Encoding_RGB8A1::TryT(unsigned int a_uiRadius)
+	{
+		Block4x4Encoding_RGB8A1 encodingTry = *this;
+
+		// init "try"
+		{
+			encodingTry.m_mode = MODE_T;
+			encodingTry.m_boolDiff = true;
+			encodingTry.m_boolFlip = false;
+			encodingTry.m_fError = FLT_MAX;
+		}
+
+		int iColor1Red = m_frgbaOriginalColor1_TAndH.IntRed(15.0f);
+		int iColor1Green = m_frgbaOriginalColor1_TAndH.IntGreen(15.0f);
+		int iColor1Blue = m_frgbaOriginalColor1_TAndH.IntBlue(15.0f);
+
+		int iMinRed1 = iColor1Red - (int)a_uiRadius;
+		if (iMinRed1 < 0)
+		{
+			iMinRed1 = 0;
+		}
+		int iMaxRed1 = iColor1Red + (int)a_uiRadius;
+		if (iMaxRed1 > 15)
+		{
+			iMinRed1 = 15;
+		}
+
+		int iMinGreen1 = iColor1Green - (int)a_uiRadius;
+		if (iMinGreen1 < 0)
+		{
+			iMinGreen1 = 0;
+		}
+		int iMaxGreen1 = iColor1Green + (int)a_uiRadius;
+		if (iMaxGreen1 > 15)
+		{
+			iMinGreen1 = 15;
+		}
+
+		int iMinBlue1 = iColor1Blue - (int)a_uiRadius;
+		if (iMinBlue1 < 0)
+		{
+			iMinBlue1 = 0;
+		}
+		int iMaxBlue1 = iColor1Blue + (int)a_uiRadius;
+		if (iMaxBlue1 > 15)
+		{
+			iMinBlue1 = 15;
+		}
+
+		int iColor2Red = m_frgbaOriginalColor2_TAndH.IntRed(15.0f);
+		int iColor2Green = m_frgbaOriginalColor2_TAndH.IntGreen(15.0f);
+		int iColor2Blue = m_frgbaOriginalColor2_TAndH.IntBlue(15.0f);
+
+		int iMinRed2 = iColor2Red - (int)a_uiRadius;
+		if (iMinRed2 < 0)
+		{
+			iMinRed2 = 0;
+		}
+		int iMaxRed2 = iColor2Red + (int)a_uiRadius;
+		if (iMaxRed2 > 15)
+		{
+			iMinRed2 = 15;
+		}
+
+		int iMinGreen2 = iColor2Green - (int)a_uiRadius;
+		if (iMinGreen2 < 0)
+		{
+			iMinGreen2 = 0;
+		}
+		int iMaxGreen2 = iColor2Green + (int)a_uiRadius;
+		if (iMaxGreen2 > 15)
+		{
+			iMinGreen2 = 15;
+		}
+
+		int iMinBlue2 = iColor2Blue - (int)a_uiRadius;
+		if (iMinBlue2 < 0)
+		{
+			iMinBlue2 = 0;
+		}
+		int iMaxBlue2 = iColor2Blue + (int)a_uiRadius;
+		if (iMaxBlue2 > 15)
+		{
+			iMinBlue2 = 15;
+		}
+
+		for (unsigned int uiDistance = 0; uiDistance < TH_DISTANCES; uiDistance++)
+		{
+			encodingTry.m_uiCW1 = uiDistance;
+
+			// twiddle m_frgbaOriginalColor2_TAndH
+			// twiddle color2 first, since it affects 3 selectors, while color1 only affects one selector
+			//
+			for (int iRed2 = iMinRed2; iRed2 <= iMaxRed2; iRed2++)
+			{
+				for (int iGreen2 = iMinGreen2; iGreen2 <= iMaxGreen2; iGreen2++)
+				{
+					for (int iBlue2 = iMinBlue2; iBlue2 <= iMaxBlue2; iBlue2++)
+					{
+						for (unsigned int uiBaseColorSwaps = 0; uiBaseColorSwaps < 2; uiBaseColorSwaps++)
+						{
+							if (uiBaseColorSwaps == 0)
+							{
+								encodingTry.m_frgbaColor1 = m_frgbaOriginalColor1_TAndH;
+								encodingTry.m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed2, (unsigned char)iGreen2, (unsigned char)iBlue2);
+							}
+							else
+							{
+								encodingTry.m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed2, (unsigned char)iGreen2, (unsigned char)iBlue2);
+								encodingTry.m_frgbaColor2 = m_frgbaOriginalColor1_TAndH;
+							}
+
+							encodingTry.TryT_BestSelectorCombination();
+
+							if (encodingTry.m_fError < m_fError)
+							{
+								m_mode = encodingTry.m_mode;
+								m_boolDiff = encodingTry.m_boolDiff;
+								m_boolFlip = encodingTry.m_boolFlip;
+
+								m_frgbaColor1 = encodingTry.m_frgbaColor1;
+								m_frgbaColor2 = encodingTry.m_frgbaColor2;
+								m_uiCW1 = encodingTry.m_uiCW1;
+
+								for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+								{
+									m_auiSelectors[uiPixel] = encodingTry.m_auiSelectors[uiPixel];
+									m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel];
+								}
+
+								m_fError = encodingTry.m_fError;
+							}
+						}
+					}
+				}
+			}
+
+			// twiddle m_frgbaOriginalColor1_TAndH
+			for (int iRed1 = iMinRed1; iRed1 <= iMaxRed1; iRed1++)
+			{
+				for (int iGreen1 = iMinGreen1; iGreen1 <= iMaxGreen1; iGreen1++)
+				{
+					for (int iBlue1 = iMinBlue1; iBlue1 <= iMaxBlue1; iBlue1++)
+					{
+						for (unsigned int uiBaseColorSwaps = 0; uiBaseColorSwaps < 2; uiBaseColorSwaps++)
+						{
+							if (uiBaseColorSwaps == 0)
+							{
+								encodingTry.m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed1, (unsigned char)iGreen1, (unsigned char)iBlue1);
+								encodingTry.m_frgbaColor2 = m_frgbaOriginalColor2_TAndH;
+							}
+							else
+							{
+								encodingTry.m_frgbaColor1 = m_frgbaOriginalColor2_TAndH;
+								encodingTry.m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed1, (unsigned char)iGreen1, (unsigned char)iBlue1);
+							}
+
+							encodingTry.TryT_BestSelectorCombination();
+
+							if (encodingTry.m_fError < m_fError)
+							{
+								m_mode = encodingTry.m_mode;
+								m_boolDiff = encodingTry.m_boolDiff;
+								m_boolFlip = encodingTry.m_boolFlip;
+
+								m_frgbaColor1 = encodingTry.m_frgbaColor1;
+								m_frgbaColor2 = encodingTry.m_frgbaColor2;
+								m_uiCW1 = encodingTry.m_uiCW1;
+
+								for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+								{
+									m_auiSelectors[uiPixel] = encodingTry.m_auiSelectors[uiPixel];
+									m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel];
+								}
+
+								m_fError = encodingTry.m_fError;
+							}
+						}
+					}
+				}
+			}
+
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// find best selector combination for TryT
+	// called on an encodingTry
+	//
+	void Block4x4Encoding_RGB8A1::TryT_BestSelectorCombination(void)
+	{
+
+		float fDistance = s_afTHDistanceTable[m_uiCW1];
+
+		unsigned int auiBestPixelSelectors[PIXELS];
+		float afBestPixelErrors[PIXELS] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX,
+			FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX };
+		ColorFloatRGBA	afrgbaBestDecodedPixels[PIXELS];
+		ColorFloatRGBA afrgbaDecodedPixel[SELECTORS];
+
+		assert(SELECTORS == 4);
+		afrgbaDecodedPixel[0] = m_frgbaColor1;
+		afrgbaDecodedPixel[1] = (m_frgbaColor2 + fDistance).ClampRGB();
+		afrgbaDecodedPixel[2] = ColorFloatRGBA();
+		afrgbaDecodedPixel[3] = (m_frgbaColor2 - fDistance).ClampRGB();
+
+		// try each selector
+		for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+		{
+			unsigned int uiMinSelector = 0;
+			unsigned int uiMaxSelector = SELECTORS - 1;
+
+			if (m_pafrgbaSource[uiPixel].fA < 0.5f)
+			{
+				uiMinSelector = 2;
+				uiMaxSelector = 2;
+			}
+
+			for (unsigned int uiSelector = uiMinSelector; uiSelector <= uiMaxSelector; uiSelector++)
+			{
+				float fPixelError = CalcPixelError(afrgbaDecodedPixel[uiSelector], m_afDecodedAlphas[uiPixel],
+													m_pafrgbaSource[uiPixel]);
+
+				if (fPixelError < afBestPixelErrors[uiPixel])
+				{
+					afBestPixelErrors[uiPixel] = fPixelError;
+					auiBestPixelSelectors[uiPixel] = uiSelector;
+					afrgbaBestDecodedPixels[uiPixel] = afrgbaDecodedPixel[uiSelector];
+				}
+			}
+		}
+		
+
+		// add up all of the pixel errors
+		float fBlockError = 0.0f;
+		for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+		{
+			fBlockError += afBestPixelErrors[uiPixel];
+		}
+
+		if (fBlockError < m_fError)
+		{
+			m_fError = fBlockError;
+
+			for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+			{
+				m_auiSelectors[uiPixel] = auiBestPixelSelectors[uiPixel];
+				m_afrgbaDecodedColors[uiPixel] = afrgbaBestDecodedPixels[uiPixel];
+			}
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// try encoding in H mode
+	// save this encoding if it improves the error
+	//
+	// since all pixels use the distance table, color1 and color2 can NOT be twiddled independently
+	// TWIDDLE_RADIUS of 2 is WAY too slow
+	//
+	void Block4x4Encoding_RGB8A1::TryH(unsigned int a_uiRadius)
+	{
+		Block4x4Encoding_RGB8A1 encodingTry = *this;
+
+		// init "try"
+		{
+			encodingTry.m_mode = MODE_H;
+			encodingTry.m_boolDiff = true;
+			encodingTry.m_boolFlip = false;
+			encodingTry.m_fError = FLT_MAX;
+		}
+
+		int iColor1Red = m_frgbaOriginalColor1_TAndH.IntRed(15.0f);
+		int iColor1Green = m_frgbaOriginalColor1_TAndH.IntGreen(15.0f);
+		int iColor1Blue = m_frgbaOriginalColor1_TAndH.IntBlue(15.0f);
+
+		int iMinRed1 = iColor1Red - (int)a_uiRadius;
+		if (iMinRed1 < 0)
+		{
+			iMinRed1 = 0;
+		}
+		int iMaxRed1 = iColor1Red + (int)a_uiRadius;
+		if (iMaxRed1 > 15)
+		{
+			iMinRed1 = 15;
+		}
+
+		int iMinGreen1 = iColor1Green - (int)a_uiRadius;
+		if (iMinGreen1 < 0)
+		{
+			iMinGreen1 = 0;
+		}
+		int iMaxGreen1 = iColor1Green + (int)a_uiRadius;
+		if (iMaxGreen1 > 15)
+		{
+			iMinGreen1 = 15;
+		}
+
+		int iMinBlue1 = iColor1Blue - (int)a_uiRadius;
+		if (iMinBlue1 < 0)
+		{
+			iMinBlue1 = 0;
+		}
+		int iMaxBlue1 = iColor1Blue + (int)a_uiRadius;
+		if (iMaxBlue1 > 15)
+		{
+			iMinBlue1 = 15;
+		}
+
+		int iColor2Red = m_frgbaOriginalColor2_TAndH.IntRed(15.0f);
+		int iColor2Green = m_frgbaOriginalColor2_TAndH.IntGreen(15.0f);
+		int iColor2Blue = m_frgbaOriginalColor2_TAndH.IntBlue(15.0f);
+
+		int iMinRed2 = iColor2Red - (int)a_uiRadius;
+		if (iMinRed2 < 0)
+		{
+			iMinRed2 = 0;
+		}
+		int iMaxRed2 = iColor2Red + (int)a_uiRadius;
+		if (iMaxRed2 > 15)
+		{
+			iMinRed2 = 15;
+		}
+
+		int iMinGreen2 = iColor2Green - (int)a_uiRadius;
+		if (iMinGreen2 < 0)
+		{
+			iMinGreen2 = 0;
+		}
+		int iMaxGreen2 = iColor2Green + (int)a_uiRadius;
+		if (iMaxGreen2 > 15)
+		{
+			iMinGreen2 = 15;
+		}
+
+		int iMinBlue2 = iColor2Blue - (int)a_uiRadius;
+		if (iMinBlue2 < 0)
+		{
+			iMinBlue2 = 0;
+		}
+		int iMaxBlue2 = iColor2Blue + (int)a_uiRadius;
+		if (iMaxBlue2 > 15)
+		{
+			iMinBlue2 = 15;
+		}
+
+		for (unsigned int uiDistance = 0; uiDistance < TH_DISTANCES; uiDistance++)
+		{
+			encodingTry.m_uiCW1 = uiDistance;
+
+			// twiddle m_frgbaOriginalColor1_TAndH
+			for (int iRed1 = iMinRed1; iRed1 <= iMaxRed1; iRed1++)
+			{
+				for (int iGreen1 = iMinGreen1; iGreen1 <= iMaxGreen1; iGreen1++)
+				{
+					for (int iBlue1 = iMinBlue1; iBlue1 <= iMaxBlue1; iBlue1++)
+					{
+						encodingTry.m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed1, (unsigned char)iGreen1, (unsigned char)iBlue1);
+						encodingTry.m_frgbaColor2 = m_frgbaOriginalColor2_TAndH;
+
+						// if color1 == color2, H encoding issues can pop up, so abort
+						if (iRed1 == iColor2Red && iGreen1 == iColor2Green && iBlue1 == iColor2Blue)
+						{
+							continue;
+						}
+
+						encodingTry.TryH_BestSelectorCombination();
+
+						if (encodingTry.m_fError < m_fError)
+						{
+							m_mode = encodingTry.m_mode;
+							m_boolDiff = encodingTry.m_boolDiff;
+							m_boolFlip = encodingTry.m_boolFlip;
+
+							m_frgbaColor1 = encodingTry.m_frgbaColor1;
+							m_frgbaColor2 = encodingTry.m_frgbaColor2;
+							m_uiCW1 = encodingTry.m_uiCW1;
+
+							for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+							{
+								m_auiSelectors[uiPixel] = encodingTry.m_auiSelectors[uiPixel];
+								m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel];
+							}
+
+							m_fError = encodingTry.m_fError;
+						}
+					}
+				}
+			}
+
+			// twiddle m_frgbaOriginalColor2_TAndH
+			for (int iRed2 = iMinRed2; iRed2 <= iMaxRed2; iRed2++)
+			{
+				for (int iGreen2 = iMinGreen2; iGreen2 <= iMaxGreen2; iGreen2++)
+				{
+					for (int iBlue2 = iMinBlue2; iBlue2 <= iMaxBlue2; iBlue2++)
+					{
+						encodingTry.m_frgbaColor1 = m_frgbaOriginalColor1_TAndH;
+						encodingTry.m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed2, (unsigned char)iGreen2, (unsigned char)iBlue2);
+
+						// if color1 == color2, H encoding issues can pop up, so abort
+						if (iRed2 == iColor1Red && iGreen2 == iColor1Green && iBlue2 == iColor1Blue)
+						{
+							continue;
+						}
+
+						encodingTry.TryH_BestSelectorCombination();
+
+						if (encodingTry.m_fError < m_fError)
+						{
+							m_mode = encodingTry.m_mode;
+							m_boolDiff = encodingTry.m_boolDiff;
+							m_boolFlip = encodingTry.m_boolFlip;
+
+							m_frgbaColor1 = encodingTry.m_frgbaColor1;
+							m_frgbaColor2 = encodingTry.m_frgbaColor2;
+							m_uiCW1 = encodingTry.m_uiCW1;
+
+							for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+							{
+								m_auiSelectors[uiPixel] = encodingTry.m_auiSelectors[uiPixel];
+								m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel];
+							}
+
+							m_fError = encodingTry.m_fError;
+						}
+					}
+				}
+			}
+
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// find best selector combination for TryH
+	// called on an encodingTry
+	//
+	void Block4x4Encoding_RGB8A1::TryH_BestSelectorCombination(void)
+	{
+
+		// abort if colors and CW will pose an encoding problem
+		{
+			unsigned int uiRed1 = (unsigned int)m_frgbaColor1.IntRed(255.0f);
+			unsigned int uiGreen1 = (unsigned int)m_frgbaColor1.IntGreen(255.0f);
+			unsigned int uiBlue1 = (unsigned int)m_frgbaColor1.IntBlue(255.0f);
+			unsigned int uiColorValue1 = (uiRed1 << 16) + (uiGreen1 << 8) + uiBlue1;
+
+			unsigned int uiRed2 = (unsigned int)m_frgbaColor2.IntRed(255.0f);
+			unsigned int uiGreen2 = (unsigned int)m_frgbaColor2.IntGreen(255.0f);
+			unsigned int uiBlue2 = (unsigned int)m_frgbaColor2.IntBlue(255.0f);
+			unsigned int uiColorValue2 = (uiRed2 << 16) + (uiGreen2 << 8) + uiBlue2;
+
+			unsigned int uiCWLsb = m_uiCW1 & 1;
+
+			if ((uiColorValue1 >= (uiColorValue2 & uiCWLsb)) == 0 ||
+				(uiColorValue1 < (uiColorValue2 & uiCWLsb)) == 1)
+			{
+				return;
+			}
+		}
+
+		float fDistance = s_afTHDistanceTable[m_uiCW1];
+
+		unsigned int auiBestPixelSelectors[PIXELS];
+		float afBestPixelErrors[PIXELS] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX,
+											FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX };
+		ColorFloatRGBA	afrgbaBestDecodedPixels[PIXELS];
+		ColorFloatRGBA afrgbaDecodedPixel[SELECTORS];
+
+		assert(SELECTORS == 4);
+		afrgbaDecodedPixel[0] = (m_frgbaColor1 + fDistance).ClampRGB();
+		afrgbaDecodedPixel[1] = (m_frgbaColor1 - fDistance).ClampRGB();
+		afrgbaDecodedPixel[2] = ColorFloatRGBA();;
+		afrgbaDecodedPixel[3] = (m_frgbaColor2 - fDistance).ClampRGB();
+
+
+		// try each selector
+		for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+		{
+			unsigned int uiMinSelector = 0;
+			unsigned int uiMaxSelector = SELECTORS - 1;
+
+			if (m_pafrgbaSource[uiPixel].fA < 0.5f)
+			{
+				uiMinSelector = 2;
+				uiMaxSelector = 2;
+			}
+
+			for (unsigned int uiSelector = uiMinSelector; uiSelector <= uiMaxSelector; uiSelector++)
+			{
+				float fPixelError = CalcPixelError(afrgbaDecodedPixel[uiSelector], m_afDecodedAlphas[uiPixel],
+													m_pafrgbaSource[uiPixel]);
+
+				if (fPixelError < afBestPixelErrors[uiPixel])
+				{
+					afBestPixelErrors[uiPixel] = fPixelError;
+					auiBestPixelSelectors[uiPixel] = uiSelector;
+					afrgbaBestDecodedPixels[uiPixel] = afrgbaDecodedPixel[uiSelector];
+				}
+			}
+		}
+		
+
+		// add up all of the pixel errors
+		float fBlockError = 0.0f;
+		for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+		{
+			fBlockError += afBestPixelErrors[uiPixel];
+		}
+
+		if (fBlockError < m_fError)
+		{
+			m_fError = fBlockError;
+
+			for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+			{
+				m_auiSelectors[uiPixel] = auiBestPixelSelectors[uiPixel];
+				m_afrgbaDecodedColors[uiPixel] = afrgbaBestDecodedPixels[uiPixel];
+			}
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// try version 1 of the degenerate search
+	// degenerate encodings use basecolor movement and a subset of the selectors to find useful encodings
+	// each subsequent version of the degenerate search uses more basecolor movement and is less likely to
+	//		be successfull
+	//
+	void Block4x4Encoding_RGB8A1::TryDegenerates1(void)
+	{
+
+		TryDifferential(m_boolMostLikelyFlip, 1, -2, 0);
+		TryDifferential(m_boolMostLikelyFlip, 1, 2, 0);
+		TryDifferential(m_boolMostLikelyFlip, 1, 0, 2);
+		TryDifferential(m_boolMostLikelyFlip, 1, 0, -2);
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// try version 2 of the degenerate search
+	// degenerate encodings use basecolor movement and a subset of the selectors to find useful encodings
+	// each subsequent version of the degenerate search uses more basecolor movement and is less likely to
+	//		be successfull
+	//
+	void Block4x4Encoding_RGB8A1::TryDegenerates2(void)
+	{
+
+		TryDifferential(!m_boolMostLikelyFlip, 1, -2, 0);
+		TryDifferential(!m_boolMostLikelyFlip, 1, 2, 0);
+		TryDifferential(!m_boolMostLikelyFlip, 1, 0, 2);
+		TryDifferential(!m_boolMostLikelyFlip, 1, 0, -2);
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// try version 3 of the degenerate search
+	// degenerate encodings use basecolor movement and a subset of the selectors to find useful encodings
+	// each subsequent version of the degenerate search uses more basecolor movement and is less likely to
+	//		be successfull
+	//
+	void Block4x4Encoding_RGB8A1::TryDegenerates3(void)
+	{
+
+		TryDifferential(m_boolMostLikelyFlip, 1, -2, -2);
+		TryDifferential(m_boolMostLikelyFlip, 1, -2, 2);
+		TryDifferential(m_boolMostLikelyFlip, 1, 2, -2);
+		TryDifferential(m_boolMostLikelyFlip, 1, 2, 2);
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// try version 4 of the degenerate search
+	// degenerate encodings use basecolor movement and a subset of the selectors to find useful encodings
+	// each subsequent version of the degenerate search uses more basecolor movement and is less likely to
+	//		be successfull
+	//
+	void Block4x4Encoding_RGB8A1::TryDegenerates4(void)
+	{
+
+		TryDifferential(m_boolMostLikelyFlip, 1, -4, 0);
+		TryDifferential(m_boolMostLikelyFlip, 1, 4, 0);
+		TryDifferential(m_boolMostLikelyFlip, 1, 0, 4);
+		TryDifferential(m_boolMostLikelyFlip, 1, 0, -4);
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// set the encoding bits based on encoding state
+	//
+	void Block4x4Encoding_RGB8A1::SetEncodingBits(void)
+	{
+		switch (m_mode)
+		{
+		case MODE_ETC1:
+			SetEncodingBits_ETC1();
+			break;
+
+		case MODE_T:
+			SetEncodingBits_T();
+			break;
+
+		case MODE_H:
+			SetEncodingBits_H();
+			break;
+
+		case MODE_PLANAR:
+			Block4x4Encoding_RGB8::SetEncodingBits_Planar();
+			break;
+
+		default:
+			assert(false);
+		}
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// set the encoding bits based on encoding state if ETC1 mode
+	//
+	void Block4x4Encoding_RGB8A1::SetEncodingBits_ETC1(void)
+	{
+
+		// there is no individual mode in RGB8A1
+		assert(m_boolDiff);
+
+		int iRed1 = m_frgbaColor1.IntRed(31.0f);
+		int iGreen1 = m_frgbaColor1.IntGreen(31.0f);
+		int iBlue1 = m_frgbaColor1.IntBlue(31.0f);
+
+		int iRed2 = m_frgbaColor2.IntRed(31.0f);
+		int iGreen2 = m_frgbaColor2.IntGreen(31.0f);
+		int iBlue2 = m_frgbaColor2.IntBlue(31.0f);
+
+		int iDRed2 = iRed2 - iRed1;
+		int iDGreen2 = iGreen2 - iGreen1;
+		int iDBlue2 = iBlue2 - iBlue1;
+
+		assert(iDRed2 >= -4 && iDRed2 < 4);
+		assert(iDGreen2 >= -4 && iDGreen2 < 4);
+		assert(iDBlue2 >= -4 && iDBlue2 < 4);
+
+		m_pencodingbitsRGB8->differential.red1 = iRed1;
+		m_pencodingbitsRGB8->differential.green1 = iGreen1;
+		m_pencodingbitsRGB8->differential.blue1 = iBlue1;
+
+		m_pencodingbitsRGB8->differential.dred2 = iDRed2;
+		m_pencodingbitsRGB8->differential.dgreen2 = iDGreen2;
+		m_pencodingbitsRGB8->differential.dblue2 = iDBlue2;
+
+		m_pencodingbitsRGB8->individual.cw1 = m_uiCW1;
+		m_pencodingbitsRGB8->individual.cw2 = m_uiCW2;
+
+		SetEncodingBits_Selectors();
+
+		// in RGB8A1 encoding bits, opaque replaces differential
+		m_pencodingbitsRGB8->differential.diff = !m_boolPunchThroughPixels;
+
+		m_pencodingbitsRGB8->individual.flip = m_boolFlip;
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// set the encoding bits based on encoding state if T mode
+	//
+	void Block4x4Encoding_RGB8A1::SetEncodingBits_T(void)
+	{
+		static const bool SANITY_CHECK = true;
+
+		assert(m_mode == MODE_T);
+		assert(m_boolDiff == true);
+
+		unsigned int uiRed1 = (unsigned int)m_frgbaColor1.IntRed(15.0f);
+		unsigned int uiGreen1 = (unsigned int)m_frgbaColor1.IntGreen(15.0f);
+		unsigned int uiBlue1 = (unsigned int)m_frgbaColor1.IntBlue(15.0f);
+
+		unsigned int uiRed2 = (unsigned int)m_frgbaColor2.IntRed(15.0f);
+		unsigned int uiGreen2 = (unsigned int)m_frgbaColor2.IntGreen(15.0f);
+		unsigned int uiBlue2 = (unsigned int)m_frgbaColor2.IntBlue(15.0f);
+
+		m_pencodingbitsRGB8->t.red1a = uiRed1 >> 2;
+		m_pencodingbitsRGB8->t.red1b = uiRed1;
+		m_pencodingbitsRGB8->t.green1 = uiGreen1;
+		m_pencodingbitsRGB8->t.blue1 = uiBlue1;
+
+		m_pencodingbitsRGB8->t.red2 = uiRed2;
+		m_pencodingbitsRGB8->t.green2 = uiGreen2;
+		m_pencodingbitsRGB8->t.blue2 = uiBlue2;
+
+		m_pencodingbitsRGB8->t.da = m_uiCW1 >> 1;
+		m_pencodingbitsRGB8->t.db = m_uiCW1;
+
+		// in RGB8A1 encoding bits, opaque replaces differential
+		m_pencodingbitsRGB8->differential.diff = !m_boolPunchThroughPixels;
+
+		Block4x4Encoding_ETC1::SetEncodingBits_Selectors();
+
+		// create an invalid R differential to trigger T mode
+		m_pencodingbitsRGB8->t.detect1 = 0;
+		m_pencodingbitsRGB8->t.detect2 = 0;
+		int iRed2 = (int)m_pencodingbitsRGB8->differential.red1 + (int)m_pencodingbitsRGB8->differential.dred2;
+		if (iRed2 >= 4)
+		{
+			m_pencodingbitsRGB8->t.detect1 = 7;
+			m_pencodingbitsRGB8->t.detect2 = 0;
+		}
+		else
+		{
+			m_pencodingbitsRGB8->t.detect1 = 0;
+			m_pencodingbitsRGB8->t.detect2 = 1;
+		}
+
+		if (SANITY_CHECK)
+		{
+			iRed2 = (int)m_pencodingbitsRGB8->differential.red1 + (int)m_pencodingbitsRGB8->differential.dred2;
+
+			// make sure red overflows
+			assert(iRed2 < 0 || iRed2 > 31);
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// set the encoding bits based on encoding state if H mode
+	//
+	// colors and selectors may need to swap in order to generate lsb of distance index
+	//
+	void Block4x4Encoding_RGB8A1::SetEncodingBits_H(void)
+	{
+		static const bool SANITY_CHECK = true;
+
+		assert(m_mode == MODE_H);
+		assert(m_boolDiff == true);
+
+		unsigned int uiRed1 = (unsigned int)m_frgbaColor1.IntRed(15.0f);
+		unsigned int uiGreen1 = (unsigned int)m_frgbaColor1.IntGreen(15.0f);
+		unsigned int uiBlue1 = (unsigned int)m_frgbaColor1.IntBlue(15.0f);
+
+		unsigned int uiRed2 = (unsigned int)m_frgbaColor2.IntRed(15.0f);
+		unsigned int uiGreen2 = (unsigned int)m_frgbaColor2.IntGreen(15.0f);
+		unsigned int uiBlue2 = (unsigned int)m_frgbaColor2.IntBlue(15.0f);
+
+		unsigned int uiColor1 = (uiRed1 << 16) + (uiGreen1 << 8) + uiBlue1;
+		unsigned int uiColor2 = (uiRed2 << 16) + (uiGreen2 << 8) + uiBlue2;
+
+		bool boolOddDistance = m_uiCW1 & 1;
+		bool boolSwapColors = (uiColor1 < uiColor2) ^ !boolOddDistance;
+
+		if (boolSwapColors)
+		{
+			m_pencodingbitsRGB8->h.red1 = uiRed2;
+			m_pencodingbitsRGB8->h.green1a = uiGreen2 >> 1;
+			m_pencodingbitsRGB8->h.green1b = uiGreen2;
+			m_pencodingbitsRGB8->h.blue1a = uiBlue2 >> 3;
+			m_pencodingbitsRGB8->h.blue1b = uiBlue2 >> 1;
+			m_pencodingbitsRGB8->h.blue1c = uiBlue2;
+
+			m_pencodingbitsRGB8->h.red2 = uiRed1;
+			m_pencodingbitsRGB8->h.green2a = uiGreen1 >> 1;
+			m_pencodingbitsRGB8->h.green2b = uiGreen1;
+			m_pencodingbitsRGB8->h.blue2 = uiBlue1;
+
+			m_pencodingbitsRGB8->h.da = m_uiCW1 >> 2;
+			m_pencodingbitsRGB8->h.db = m_uiCW1 >> 1;
+		}
+		else
+		{
+			m_pencodingbitsRGB8->h.red1 = uiRed1;
+			m_pencodingbitsRGB8->h.green1a = uiGreen1 >> 1;
+			m_pencodingbitsRGB8->h.green1b = uiGreen1;
+			m_pencodingbitsRGB8->h.blue1a = uiBlue1 >> 3;
+			m_pencodingbitsRGB8->h.blue1b = uiBlue1 >> 1;
+			m_pencodingbitsRGB8->h.blue1c = uiBlue1;
+
+			m_pencodingbitsRGB8->h.red2 = uiRed2;
+			m_pencodingbitsRGB8->h.green2a = uiGreen2 >> 1;
+			m_pencodingbitsRGB8->h.green2b = uiGreen2;
+			m_pencodingbitsRGB8->h.blue2 = uiBlue2;
+
+			m_pencodingbitsRGB8->h.da = m_uiCW1 >> 2;
+			m_pencodingbitsRGB8->h.db = m_uiCW1 >> 1;
+		}
+
+		// in RGB8A1 encoding bits, opaque replaces differential
+		m_pencodingbitsRGB8->differential.diff = !m_boolPunchThroughPixels;
+
+		Block4x4Encoding_ETC1::SetEncodingBits_Selectors();
+
+		if (boolSwapColors)
+		{
+			m_pencodingbitsRGB8->h.selectors ^= 0x0000FFFF;
+		}
+
+		// create an invalid R differential to trigger T mode
+		m_pencodingbitsRGB8->h.detect1 = 0;
+		m_pencodingbitsRGB8->h.detect2 = 0;
+		m_pencodingbitsRGB8->h.detect3 = 0;
+		int iRed2 = (int)m_pencodingbitsRGB8->differential.red1 + (int)m_pencodingbitsRGB8->differential.dred2;
+		int iGreen2 = (int)m_pencodingbitsRGB8->differential.green1 + (int)m_pencodingbitsRGB8->differential.dgreen2;
+		if (iRed2 < 0 || iRed2 > 31)
+		{
+			m_pencodingbitsRGB8->h.detect1 = 1;
+		}
+		if (iGreen2 >= 4)
+		{
+			m_pencodingbitsRGB8->h.detect2 = 7;
+			m_pencodingbitsRGB8->h.detect3 = 0;
+		}
+		else
+		{
+			m_pencodingbitsRGB8->h.detect2 = 0;
+			m_pencodingbitsRGB8->h.detect3 = 1;
+		}
+
+		if (SANITY_CHECK)
+		{
+			iRed2 = (int)m_pencodingbitsRGB8->differential.red1 + (int)m_pencodingbitsRGB8->differential.dred2;
+			iGreen2 = (int)m_pencodingbitsRGB8->differential.green1 + (int)m_pencodingbitsRGB8->differential.dgreen2;
+
+			// make sure red doesn't overflow and green does
+			assert(iRed2 >= 0 && iRed2 <= 31);
+			assert(iGreen2 < 0 || iGreen2 > 31);
+		}
+
+	}
+
+	// ####################################################################################################
+	// Block4x4Encoding_RGB8A1_Opaque
+	// ####################################################################################################
+
+	// ----------------------------------------------------------------------------------------------------
+	// perform a single encoding iteration
+	// replace the encoding if a better encoding was found
+	// subsequent iterations generally take longer for each iteration
+	// set m_boolDone if encoding is perfect or encoding is finished based on a_fEffort
+	//
+	void Block4x4Encoding_RGB8A1_Opaque::PerformIteration(float a_fEffort)
+	{
+		assert(!m_boolPunchThroughPixels);
+		assert(!m_boolTransparent);
+		assert(!m_boolDone);
+
+		switch (m_uiEncodingIterations)
+		{
+		case 0:
+			PerformFirstIteration();
+			break;
+
+		case 1:
+			Block4x4Encoding_ETC1::TryDifferential(m_boolMostLikelyFlip, 1, 0, 0);
+			break;
+
+		case 2:
+			Block4x4Encoding_ETC1::TryDifferential(!m_boolMostLikelyFlip, 1, 0, 0);
+			break;
+
+		case 3:
+			Block4x4Encoding_RGB8::TryPlanar(1);
+			break;
+
+		case 4:
+			Block4x4Encoding_RGB8::TryTAndH(1);
+			if (a_fEffort <= 49.5f)
+			{
+				m_boolDone = true;
+			}
+			break;
+
+		case 5:
+			Block4x4Encoding_ETC1::TryDegenerates1();
+			if (a_fEffort <= 59.5f)
+			{
+				m_boolDone = true;
+			}
+			break;
+
+		case 6:
+			Block4x4Encoding_ETC1::TryDegenerates2();
+			if (a_fEffort <= 69.5f)
+			{
+				m_boolDone = true;
+			}
+			break;
+
+		case 7:
+			Block4x4Encoding_ETC1::TryDegenerates3();
+			if (a_fEffort <= 79.5f)
+			{
+				m_boolDone = true;
+			}
+			break;
+
+		case 8:
+			Block4x4Encoding_ETC1::TryDegenerates4();
+			m_boolDone = true;
+			break;
+
+		default:
+			assert(0);
+			break;
+		}
+
+		m_uiEncodingIterations++;
+		SetDoneIfPerfect();
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// find best initial encoding to ensure block has a valid encoding
+	//
+	void Block4x4Encoding_RGB8A1_Opaque::PerformFirstIteration(void)
+	{
+		
+		// set decoded alphas
+		// calculate alpha error
+		m_fError = 0.0f;
+		for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+		{
+			m_afDecodedAlphas[uiPixel] = 1.0f;
+
+			float fDeltaA = 1.0f - m_pafrgbaSource[uiPixel].fA;
+			m_fError += fDeltaA * fDeltaA;
+		}
+
+		CalculateMostLikelyFlip();
+
+		m_fError = FLT_MAX;
+
+		Block4x4Encoding_ETC1::TryDifferential(m_boolMostLikelyFlip, 0, 0, 0);
+		SetDoneIfPerfect();
+		if (m_boolDone)
+		{
+			return;
+		}
+		Block4x4Encoding_ETC1::TryDifferential(!m_boolMostLikelyFlip, 0, 0, 0);
+		SetDoneIfPerfect();
+		if (m_boolDone)
+		{
+			return;
+		}
+		Block4x4Encoding_RGB8::TryPlanar(0);
+		SetDoneIfPerfect();
+		if (m_boolDone)
+		{
+			return;
+		}
+		Block4x4Encoding_RGB8::TryTAndH(0);
+		SetDoneIfPerfect();
+	}
+
+	// ####################################################################################################
+	// Block4x4Encoding_RGB8A1_Transparent
+	// ####################################################################################################
+
+	// ----------------------------------------------------------------------------------------------------
+	// perform a single encoding iteration
+	// replace the encoding if a better encoding was found
+	// subsequent iterations generally take longer for each iteration
+	// set m_boolDone if encoding is perfect or encoding is finished based on a_fEffort
+	//
+	void Block4x4Encoding_RGB8A1_Transparent::PerformIteration(float )
+	{
+		assert(!m_boolOpaque);
+		assert(m_boolTransparent);
+		assert(!m_boolDone);
+		assert(m_uiEncodingIterations == 0);
+
+		m_mode = MODE_ETC1;
+		m_boolDiff = true;
+		m_boolFlip = false;
+
+		m_uiCW1 = 0;
+		m_uiCW2 = 0;
+
+		m_frgbaColor1 = ColorFloatRGBA();
+		m_frgbaColor2 = ColorFloatRGBA();
+
+		for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+		{
+			m_auiSelectors[uiPixel] = TRANSPARENT_SELECTOR;
+
+			m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA();
+			m_afDecodedAlphas[uiPixel] = 0.0f;
+		}
+
+		CalcBlockError();
+
+		m_boolDone = true;
+		m_uiEncodingIterations++;
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+}

+ 129 - 0
thirdparty/etc2comp/EtcBlock4x4Encoding_RGB8A1.h

@@ -0,0 +1,129 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "EtcBlock4x4Encoding_RGB8.h"
+#include "EtcErrorMetric.h"
+#include "EtcBlock4x4EncodingBits.h"
+
+namespace Etc
+{
+
+	// ################################################################################
+	// Block4x4Encoding_RGB8A1
+	// RGB8A1 if not completely opaque or transparent
+	// ################################################################################
+
+	class Block4x4Encoding_RGB8A1 : public Block4x4Encoding_RGB8
+	{
+	public:
+
+		static const unsigned int TRANSPARENT_SELECTOR = 2;
+
+		Block4x4Encoding_RGB8A1(void);
+		virtual ~Block4x4Encoding_RGB8A1(void);
+
+		virtual void InitFromSource(Block4x4 *a_pblockParent,
+									ColorFloatRGBA *a_pafrgbaSource,
+									unsigned char *a_paucEncodingBits,
+									ErrorMetric a_errormetric);
+
+		virtual void InitFromEncodingBits(Block4x4 *a_pblockParent,
+											unsigned char *a_paucEncodingBits,
+											ColorFloatRGBA *a_pafrgbaSource,
+											ErrorMetric a_errormetric);
+
+		virtual void PerformIteration(float a_fEffort);
+
+		virtual void SetEncodingBits(void);
+
+		void InitFromEncodingBits_ETC1(Block4x4 *a_pblockParent,
+										unsigned char *a_paucEncodingBits,
+										ColorFloatRGBA *a_pafrgbaSource,
+										ErrorMetric a_errormetric);
+
+		void InitFromEncodingBits_T(void);
+		void InitFromEncodingBits_H(void);
+
+		void PerformFirstIteration(void);
+
+		void Decode_ETC1(void);
+		void DecodePixels_T(void);
+		void DecodePixels_H(void);
+		void SetEncodingBits_ETC1(void);
+		void SetEncodingBits_T(void);
+		void SetEncodingBits_H(void);
+
+	protected:
+
+		bool m_boolOpaque;				// all source pixels have alpha >= 0.5
+		bool m_boolTransparent;			// all source pixels have alpha < 0.5
+		bool m_boolPunchThroughPixels;	// some source pixels have alpha < 0.5
+
+		static float s_aafCwOpaqueUnsetTable[CW_RANGES][SELECTORS];
+
+	private:
+
+		void TryDifferential(bool a_boolFlip, unsigned int a_uiRadius,
+								int a_iGrayOffset1, int a_iGrayOffset2);
+		void TryDifferentialHalf(DifferentialTrys::Half *a_phalf);
+
+		void TryT(unsigned int a_uiRadius);
+		void TryT_BestSelectorCombination(void);
+		void TryH(unsigned int a_uiRadius);
+		void TryH_BestSelectorCombination(void);
+
+		void TryDegenerates1(void);
+		void TryDegenerates2(void);
+		void TryDegenerates3(void);
+		void TryDegenerates4(void);
+
+	};
+
+	// ################################################################################
+	// Block4x4Encoding_RGB8A1_Opaque
+	// RGB8A1 if all pixels have alpha==1
+	// ################################################################################
+
+	class Block4x4Encoding_RGB8A1_Opaque : public Block4x4Encoding_RGB8A1
+	{
+	public:
+
+		virtual void PerformIteration(float a_fEffort);
+
+		void PerformFirstIteration(void);
+
+	private:
+
+	};
+
+	// ################################################################################
+	// Block4x4Encoding_RGB8A1_Transparent
+	// RGB8A1 if all pixels have alpha==0
+	// ################################################################################
+
+	class Block4x4Encoding_RGB8A1_Transparent : public Block4x4Encoding_RGB8A1
+	{
+	public:
+
+		virtual void PerformIteration(float a_fEffort);
+
+	private:
+
+	};
+
+} // namespace Etc

+ 474 - 0
thirdparty/etc2comp/EtcBlock4x4Encoding_RGBA8.cpp

@@ -0,0 +1,474 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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.
+ */
+
+/*
+EtcBlock4x4Encoding_RGBA8.cpp contains:
+	Block4x4Encoding_RGBA8
+	Block4x4Encoding_RGBA8_Opaque
+	Block4x4Encoding_RGBA8_Transparent
+
+These encoders are used when targetting file format RGBA8.
+
+Block4x4Encoding_RGBA8_Opaque is used when all pixels in the 4x4 block are opaque
+Block4x4Encoding_RGBA8_Transparent is used when all pixels in the 4x4 block are transparent
+Block4x4Encoding_RGBA8 is used when there is a mixture of alphas in the 4x4 block
+
+*/
+
+#include "EtcConfig.h"
+#include "EtcBlock4x4Encoding_RGBA8.h"
+
+#include "EtcBlock4x4EncodingBits.h"
+#include "EtcBlock4x4.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <float.h>
+#include <limits>
+
+namespace Etc
+{
+
+	// ####################################################################################################
+	// Block4x4Encoding_RGBA8
+	// ####################################################################################################
+
+	float Block4x4Encoding_RGBA8::s_aafModifierTable[MODIFIER_TABLE_ENTRYS][ALPHA_SELECTORS]
+	{
+		{ -3.0f / 255.0f, -6.0f / 255.0f,  -9.0f / 255.0f, -15.0f / 255.0f, 2.0f / 255.0f, 5.0f / 255.0f, 8.0f / 255.0f, 14.0f / 255.0f },
+		{ -3.0f / 255.0f, -7.0f / 255.0f, -10.0f / 255.0f, -13.0f / 255.0f, 2.0f / 255.0f, 6.0f / 255.0f, 9.0f / 255.0f, 12.0f / 255.0f },
+		{ -2.0f / 255.0f, -5.0f / 255.0f,  -8.0f / 255.0f, -13.0f / 255.0f, 1.0f / 255.0f, 4.0f / 255.0f, 7.0f / 255.0f, 12.0f / 255.0f },
+		{ -2.0f / 255.0f, -4.0f / 255.0f,  -6.0f / 255.0f, -13.0f / 255.0f, 1.0f / 255.0f, 3.0f / 255.0f, 5.0f / 255.0f, 12.0f / 255.0f },
+
+		{ -3.0f / 255.0f, -6.0f / 255.0f,  -8.0f / 255.0f, -12.0f / 255.0f, 2.0f / 255.0f, 5.0f / 255.0f, 7.0f / 255.0f, 11.0f / 255.0f },
+		{ -3.0f / 255.0f, -7.0f / 255.0f,  -9.0f / 255.0f, -11.0f / 255.0f, 2.0f / 255.0f, 6.0f / 255.0f, 8.0f / 255.0f, 10.0f / 255.0f },
+		{ -4.0f / 255.0f, -7.0f / 255.0f,  -8.0f / 255.0f, -11.0f / 255.0f, 3.0f / 255.0f, 6.0f / 255.0f, 7.0f / 255.0f, 10.0f / 255.0f },
+		{ -3.0f / 255.0f, -5.0f / 255.0f,  -8.0f / 255.0f, -11.0f / 255.0f, 2.0f / 255.0f, 4.0f / 255.0f, 7.0f / 255.0f, 10.0f / 255.0f },
+
+		{ -2.0f / 255.0f, -6.0f / 255.0f,  -8.0f / 255.0f, -10.0f / 255.0f, 1.0f / 255.0f, 5.0f / 255.0f, 7.0f / 255.0f,  9.0f / 255.0f },
+		{ -2.0f / 255.0f, -5.0f / 255.0f,  -8.0f / 255.0f, -10.0f / 255.0f, 1.0f / 255.0f, 4.0f / 255.0f, 7.0f / 255.0f,  9.0f / 255.0f },
+		{ -2.0f / 255.0f, -4.0f / 255.0f,  -8.0f / 255.0f, -10.0f / 255.0f, 1.0f / 255.0f, 3.0f / 255.0f, 7.0f / 255.0f,  9.0f / 255.0f },
+		{ -2.0f / 255.0f, -5.0f / 255.0f,  -7.0f / 255.0f, -10.0f / 255.0f, 1.0f / 255.0f, 4.0f / 255.0f, 6.0f / 255.0f,  9.0f / 255.0f },
+
+		{ -3.0f / 255.0f, -4.0f / 255.0f,  -7.0f / 255.0f, -10.0f / 255.0f, 2.0f / 255.0f, 3.0f / 255.0f, 6.0f / 255.0f,  9.0f / 255.0f },
+		{ -1.0f / 255.0f, -2.0f / 255.0f,  -3.0f / 255.0f, -10.0f / 255.0f, 0.0f / 255.0f, 1.0f / 255.0f, 2.0f / 255.0f,  9.0f / 255.0f },
+		{ -4.0f / 255.0f, -6.0f / 255.0f,  -8.0f / 255.0f,  -9.0f / 255.0f, 3.0f / 255.0f, 5.0f / 255.0f, 7.0f / 255.0f,  8.0f / 255.0f },
+		{ -3.0f / 255.0f, -5.0f / 255.0f,  -7.0f / 255.0f,  -9.0f / 255.0f, 2.0f / 255.0f, 4.0f / 255.0f, 6.0f / 255.0f,  8.0f / 255.0f }
+	};
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+	Block4x4Encoding_RGBA8::Block4x4Encoding_RGBA8(void)
+	{
+
+		m_pencodingbitsA8 = nullptr;
+
+	}
+	Block4x4Encoding_RGBA8::~Block4x4Encoding_RGBA8(void) {}
+	// ----------------------------------------------------------------------------------------------------
+	// initialization prior to encoding
+	// a_pblockParent points to the block associated with this encoding
+	// a_errormetric is used to choose the best encoding
+	// a_pafrgbaSource points to a 4x4 block subset of the source image
+	// a_paucEncodingBits points to the final encoding bits
+	//
+	void Block4x4Encoding_RGBA8::InitFromSource(Block4x4 *a_pblockParent,
+												ColorFloatRGBA *a_pafrgbaSource,
+												unsigned char *a_paucEncodingBits, ErrorMetric a_errormetric)
+	{
+		Block4x4Encoding::Init(a_pblockParent, a_pafrgbaSource,a_errormetric);
+
+		m_pencodingbitsA8 = (Block4x4EncodingBits_A8 *)a_paucEncodingBits;
+		m_pencodingbitsRGB8 = (Block4x4EncodingBits_RGB8 *)(a_paucEncodingBits + sizeof(Block4x4EncodingBits_A8));
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// initialization from the encoding bits of a previous encoding
+	// a_pblockParent points to the block associated with this encoding
+	// a_errormetric is used to choose the best encoding
+	// a_pafrgbaSource points to a 4x4 block subset of the source image
+	// a_paucEncodingBits points to the final encoding bits of a previous encoding
+	//
+	void Block4x4Encoding_RGBA8::InitFromEncodingBits(Block4x4 *a_pblockParent,
+														unsigned char *a_paucEncodingBits,
+														ColorFloatRGBA *a_pafrgbaSource,
+														ErrorMetric a_errormetric)
+	{
+
+		m_pencodingbitsA8 = (Block4x4EncodingBits_A8 *)a_paucEncodingBits;
+		m_pencodingbitsRGB8 = (Block4x4EncodingBits_RGB8 *)(a_paucEncodingBits + sizeof(Block4x4EncodingBits_A8));
+
+		// init RGB portion
+		Block4x4Encoding_RGB8::InitFromEncodingBits(a_pblockParent,
+													(unsigned char *) m_pencodingbitsRGB8,
+													a_pafrgbaSource,
+													a_errormetric);
+
+		// init A8 portion
+		// has to be done after InitFromEncodingBits()
+		{
+			m_fBase = m_pencodingbitsA8->data.base / 255.0f;
+			m_fMultiplier = (float)m_pencodingbitsA8->data.multiplier;
+			m_uiModifierTableIndex = m_pencodingbitsA8->data.table;
+
+			unsigned long long int ulliSelectorBits = 0;
+			ulliSelectorBits |= (unsigned long long int)m_pencodingbitsA8->data.selectors0 << 40;
+			ulliSelectorBits |= (unsigned long long int)m_pencodingbitsA8->data.selectors1 << 32;
+			ulliSelectorBits |= (unsigned long long int)m_pencodingbitsA8->data.selectors2 << 24;
+			ulliSelectorBits |= (unsigned long long int)m_pencodingbitsA8->data.selectors3 << 16;
+			ulliSelectorBits |= (unsigned long long int)m_pencodingbitsA8->data.selectors4 << 8;
+			ulliSelectorBits |= (unsigned long long int)m_pencodingbitsA8->data.selectors5;
+			for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+			{
+				unsigned int uiShift = 45 - (3 * uiPixel);
+				m_auiAlphaSelectors[uiPixel] = (ulliSelectorBits >> uiShift) & (ALPHA_SELECTORS - 1);
+			}
+
+			// decode the alphas
+			// calc alpha error
+			m_fError = 0.0f;
+			for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+			{
+				m_afDecodedAlphas[uiPixel] = DecodePixelAlpha(m_fBase, m_fMultiplier,
+					m_uiModifierTableIndex,
+					m_auiAlphaSelectors[uiPixel]);
+
+				float fDeltaAlpha = m_afDecodedAlphas[uiPixel] - m_pafrgbaSource[uiPixel].fA;
+				m_fError += fDeltaAlpha * fDeltaAlpha;
+			}
+		}
+
+		// redo error calc to include alpha
+		CalcBlockError();
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// perform a single encoding iteration
+	// replace the encoding if a better encoding was found
+	// subsequent iterations generally take longer for each iteration
+	// set m_boolDone if encoding is perfect or encoding is finished based on a_fEffort
+	//
+	// similar to Block4x4Encoding_RGB8_Base::Encode_RGB8(), but with alpha added
+	//
+	void Block4x4Encoding_RGBA8::PerformIteration(float a_fEffort)
+	{
+		assert(!m_boolDone);
+
+		if (m_uiEncodingIterations == 0)
+		{
+			if (a_fEffort < 24.9f)
+			{
+				CalculateA8(0.0f);
+			}
+			else if (a_fEffort < 49.9f)
+			{
+				CalculateA8(1.0f);
+			}
+			else
+			{
+				CalculateA8(2.0f);
+			}
+		}
+
+		Block4x4Encoding_RGB8::PerformIteration(a_fEffort);
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// find the best combination of base alpga, multiplier and selectors
+	//
+	// a_fRadius limits the range of base alpha to try
+	//
+	void Block4x4Encoding_RGBA8::CalculateA8(float a_fRadius)
+	{
+
+		// find min/max alpha
+		float fMinAlpha = 1.0f;
+		float fMaxAlpha = 0.0f;
+		for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+		{
+			float fAlpha = m_pafrgbaSource[uiPixel].fA;
+
+			// ignore border pixels
+			if (isnan(fAlpha))
+			{
+				continue;
+			}
+
+			if (fAlpha < fMinAlpha)
+			{
+				fMinAlpha = fAlpha;
+			}
+			if (fAlpha > fMaxAlpha)
+			{
+				fMaxAlpha = fAlpha;
+			}
+		}
+		assert(fMinAlpha <= fMaxAlpha);
+
+		float fAlphaRange = fMaxAlpha - fMinAlpha;
+
+		// try each modifier table entry
+		m_fError = FLT_MAX;		// artificially high value
+		for (unsigned int uiTableEntry = 0; uiTableEntry < MODIFIER_TABLE_ENTRYS; uiTableEntry++)
+		{
+			static const unsigned int MIN_VALUE_SELECTOR = 3;
+			static const unsigned int MAX_VALUE_SELECTOR = 7;
+
+			float fTableEntryCenter = -s_aafModifierTable[uiTableEntry][MIN_VALUE_SELECTOR];
+
+			float fTableEntryRange = s_aafModifierTable[uiTableEntry][MAX_VALUE_SELECTOR] -
+				s_aafModifierTable[uiTableEntry][MIN_VALUE_SELECTOR];
+
+			float fCenterRatio = fTableEntryCenter / fTableEntryRange;
+
+			float fCenter = fMinAlpha + fCenterRatio*fAlphaRange;
+			fCenter = roundf(255.0f * fCenter) / 255.0f;
+
+			float fMinBase = fCenter - (a_fRadius / 255.0f);
+			if (fMinBase < 0.0f)
+			{
+				fMinBase = 0.0f;
+			}
+
+			float fMaxBase = fCenter + (a_fRadius / 255.0f);
+			if (fMaxBase > 1.0f)
+			{
+				fMaxBase = 1.0f;
+			}
+
+			for (float fBase = fMinBase; fBase <= fMaxBase; fBase += (0.999999f / 255.0f))
+			{
+
+				float fRangeMultiplier = roundf(fAlphaRange / fTableEntryRange);
+
+				float fMinMultiplier = fRangeMultiplier - a_fRadius;
+				if (fMinMultiplier < 1.0f)
+				{
+					fMinMultiplier = 1.0f;
+				}
+				else if (fMinMultiplier > 15.0f)
+				{
+					fMinMultiplier = 15.0f;
+				}
+
+				float fMaxMultiplier = fRangeMultiplier + a_fRadius;
+				if (fMaxMultiplier < 1.0f)
+				{
+					fMaxMultiplier = 1.0f;
+				}
+				else if (fMaxMultiplier > 15.0f)
+				{
+					fMaxMultiplier = 15.0f;
+				}
+
+				for (float fMultiplier = fMinMultiplier; fMultiplier <= fMaxMultiplier; fMultiplier += 1.0f)
+				{
+					// find best selector for each pixel
+					unsigned int auiBestSelectors[PIXELS];
+					float afBestAlphaError[PIXELS];
+					float afBestDecodedAlphas[PIXELS];
+					for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+					{
+						float fBestPixelAlphaError = FLT_MAX;
+						for (unsigned int uiSelector = 0; uiSelector < ALPHA_SELECTORS; uiSelector++)
+						{
+							float fDecodedAlpha = DecodePixelAlpha(fBase, fMultiplier, uiTableEntry, uiSelector);
+
+							// border pixels (NAN) should have zero error
+							float fPixelDeltaAlpha = isnan(m_pafrgbaSource[uiPixel].fA) ?
+															0.0f :
+															fDecodedAlpha - m_pafrgbaSource[uiPixel].fA;
+
+							float fPixelAlphaError = fPixelDeltaAlpha * fPixelDeltaAlpha;
+
+							if (fPixelAlphaError < fBestPixelAlphaError)
+							{
+								fBestPixelAlphaError = fPixelAlphaError;
+								auiBestSelectors[uiPixel] = uiSelector;
+								afBestAlphaError[uiPixel] = fBestPixelAlphaError;
+								afBestDecodedAlphas[uiPixel] = fDecodedAlpha;
+							}
+						}
+					}
+
+					float fBlockError = 0.0f;
+					for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+					{
+						fBlockError += afBestAlphaError[uiPixel];
+					}
+
+					if (fBlockError < m_fError)
+					{
+						m_fError = fBlockError;
+
+						m_fBase = fBase;
+						m_fMultiplier = fMultiplier;
+						m_uiModifierTableIndex = uiTableEntry;
+						for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+						{
+							m_auiAlphaSelectors[uiPixel] = auiBestSelectors[uiPixel];
+							m_afDecodedAlphas[uiPixel] = afBestDecodedAlphas[uiPixel];
+						}
+					}
+				}
+			}
+
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// set the encoding bits based on encoding state
+	//
+	void Block4x4Encoding_RGBA8::SetEncodingBits(void)
+	{
+
+		// set the RGB8 portion
+		Block4x4Encoding_RGB8::SetEncodingBits();
+
+		// set the A8 portion
+		{
+			m_pencodingbitsA8->data.base = (unsigned char)roundf(255.0f * m_fBase);
+			m_pencodingbitsA8->data.table = m_uiModifierTableIndex;
+			m_pencodingbitsA8->data.multiplier = (unsigned char)roundf(m_fMultiplier);
+
+			unsigned long long int ulliSelectorBits = 0;
+			for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+			{
+				unsigned int uiShift = 45 - (3 * uiPixel);
+				ulliSelectorBits |= ((unsigned long long int)m_auiAlphaSelectors[uiPixel]) << uiShift;
+			}
+
+			m_pencodingbitsA8->data.selectors0 = ulliSelectorBits >> 40;
+			m_pencodingbitsA8->data.selectors1 = ulliSelectorBits >> 32;
+			m_pencodingbitsA8->data.selectors2 = ulliSelectorBits >> 24;
+			m_pencodingbitsA8->data.selectors3 = ulliSelectorBits >> 16;
+			m_pencodingbitsA8->data.selectors4 = ulliSelectorBits >> 8;
+			m_pencodingbitsA8->data.selectors5 = ulliSelectorBits;
+		}
+
+	}
+
+	// ####################################################################################################
+	// Block4x4Encoding_RGBA8_Opaque
+	// ####################################################################################################
+
+	// ----------------------------------------------------------------------------------------------------
+	// perform a single encoding iteration
+	// replace the encoding if a better encoding was found
+	// subsequent iterations generally take longer for each iteration
+	// set m_boolDone if encoding is perfect or encoding is finished based on a_fEffort
+	//
+	void Block4x4Encoding_RGBA8_Opaque::PerformIteration(float a_fEffort)
+	{
+		assert(!m_boolDone);
+
+		if (m_uiEncodingIterations == 0)
+		{
+			m_fError = 0.0f;
+
+			for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+			{
+				m_afDecodedAlphas[uiPixel] = 1.0f;
+			}
+		}
+
+		Block4x4Encoding_RGB8::PerformIteration(a_fEffort);
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// set the encoding bits based on encoding state
+	//
+	void Block4x4Encoding_RGBA8_Opaque::SetEncodingBits(void)
+	{
+
+		// set the RGB8 portion
+		Block4x4Encoding_RGB8::SetEncodingBits();
+
+		// set the A8 portion
+		m_pencodingbitsA8->data.base = 255;
+		m_pencodingbitsA8->data.table = 15;
+		m_pencodingbitsA8->data.multiplier = 15;
+		m_pencodingbitsA8->data.selectors0 = 0xFF;
+		m_pencodingbitsA8->data.selectors1 = 0xFF;
+		m_pencodingbitsA8->data.selectors2 = 0xFF;
+		m_pencodingbitsA8->data.selectors3 = 0xFF;
+		m_pencodingbitsA8->data.selectors4 = 0xFF;
+		m_pencodingbitsA8->data.selectors5 = 0xFF;
+
+	}
+
+	// ####################################################################################################
+	// Block4x4Encoding_RGBA8_Transparent
+	// ####################################################################################################
+
+	// ----------------------------------------------------------------------------------------------------
+	// perform a single encoding iteration
+	// replace the encoding if a better encoding was found
+	// subsequent iterations generally take longer for each iteration
+	// set m_boolDone if encoding is perfect or encoding is finished based on a_fEffort
+	//
+	void Block4x4Encoding_RGBA8_Transparent::PerformIteration(float )
+	{
+		assert(!m_boolDone);
+		assert(m_uiEncodingIterations == 0);
+
+		m_mode = MODE_ETC1;
+		m_boolDiff = true;
+		m_boolFlip = false;
+
+		for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
+		{
+			m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA();
+			m_afDecodedAlphas[uiPixel] = 0.0f;
+		}
+
+		m_fError = 0.0f;
+
+		m_boolDone = true;
+		m_uiEncodingIterations++;
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// set the encoding bits based on encoding state
+	//
+	void Block4x4Encoding_RGBA8_Transparent::SetEncodingBits(void)
+	{
+
+		Block4x4Encoding_RGB8::SetEncodingBits();
+
+		// set the A8 portion
+		m_pencodingbitsA8->data.base = 0;
+		m_pencodingbitsA8->data.table = 0;
+		m_pencodingbitsA8->data.multiplier = 1;
+		m_pencodingbitsA8->data.selectors0 = 0;
+		m_pencodingbitsA8->data.selectors1 = 0;
+		m_pencodingbitsA8->data.selectors2 = 0;
+		m_pencodingbitsA8->data.selectors3 = 0;
+		m_pencodingbitsA8->data.selectors4 = 0;
+		m_pencodingbitsA8->data.selectors5 = 0;
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+}

+ 121 - 0
thirdparty/etc2comp/EtcBlock4x4Encoding_RGBA8.h

@@ -0,0 +1,121 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "EtcBlock4x4Encoding_RGB8.h"
+
+namespace Etc
+{
+	class Block4x4EncodingBits_A8;
+
+	// ################################################################################
+	// Block4x4Encoding_RGBA8
+	// RGBA8 if not completely opaque or transparent
+	// ################################################################################
+
+	class Block4x4Encoding_RGBA8 : public Block4x4Encoding_RGB8
+	{
+	public:
+
+		Block4x4Encoding_RGBA8(void);
+		virtual ~Block4x4Encoding_RGBA8(void);
+
+		virtual void InitFromSource(Block4x4 *a_pblockParent,
+									ColorFloatRGBA *a_pafrgbaSource,
+									unsigned char *a_paucEncodingBits, ErrorMetric a_errormetric);
+
+		virtual void InitFromEncodingBits(Block4x4 *a_pblockParent,
+											unsigned char *a_paucEncodingBits,
+											ColorFloatRGBA *a_pafrgbaSource,
+											ErrorMetric a_errormetric);
+
+		virtual void PerformIteration(float a_fEffort);
+
+		virtual void SetEncodingBits(void);
+
+	protected:
+
+		static const unsigned int MODIFIER_TABLE_ENTRYS = 16;
+		static const unsigned int ALPHA_SELECTOR_BITS = 3;
+		static const unsigned int ALPHA_SELECTORS = 1 << ALPHA_SELECTOR_BITS;
+
+		static float s_aafModifierTable[MODIFIER_TABLE_ENTRYS][ALPHA_SELECTORS];
+
+		void CalculateA8(float a_fRadius);
+
+		Block4x4EncodingBits_A8 *m_pencodingbitsA8;	// A8 portion of Block4x4EncodingBits_RGBA8
+
+		float m_fBase;
+		float m_fMultiplier;
+		unsigned int m_uiModifierTableIndex;
+		unsigned int m_auiAlphaSelectors[PIXELS];
+
+	private:
+
+		inline float DecodePixelAlpha(float a_fBase, float a_fMultiplier,
+										unsigned int a_uiTableIndex, unsigned int a_uiSelector)
+		{
+			float fPixelAlpha = a_fBase + 
+								a_fMultiplier*s_aafModifierTable[a_uiTableIndex][a_uiSelector];
+			if (fPixelAlpha < 0.0f)
+			{
+				fPixelAlpha = 0.0f;
+			}
+			else if (fPixelAlpha > 1.0f)
+			{
+				fPixelAlpha = 1.0f;
+			}
+
+			return fPixelAlpha;
+		}
+
+	};
+
+	// ################################################################################
+	// Block4x4Encoding_RGBA8_Opaque
+	// RGBA8 if all pixels have alpha==1
+	// ################################################################################
+
+	class Block4x4Encoding_RGBA8_Opaque : public Block4x4Encoding_RGBA8
+	{
+	public:
+
+		virtual void PerformIteration(float a_fEffort);
+
+		virtual void SetEncodingBits(void);
+
+	};
+
+	// ################################################################################
+	// Block4x4Encoding_RGBA8_Transparent
+	// RGBA8 if all pixels have alpha==0
+	// ################################################################################
+
+	class Block4x4Encoding_RGBA8_Transparent : public Block4x4Encoding_RGBA8
+	{
+	public:
+
+		virtual void PerformIteration(float a_fEffort);
+
+		virtual void SetEncodingBits(void);
+
+	};
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+
+} // namespace Etc

+ 64 - 0
thirdparty/etc2comp/EtcColor.h

@@ -0,0 +1,64 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <math.h>
+
+namespace Etc
+{
+
+	inline float LogToLinear(float a_fLog)
+	{
+		static const float ALPHA = 0.055f;
+		static const float ONE_PLUS_ALPHA = 1.0f + ALPHA;
+
+		if (a_fLog <= 0.04045f)
+		{
+			return a_fLog / 12.92f;
+		}
+		else
+		{
+			return powf((a_fLog + ALPHA) / ONE_PLUS_ALPHA, 2.4f);
+		}
+	}
+
+	inline float LinearToLog(float &a_fLinear)
+	{
+		static const float ALPHA = 0.055f;
+		static const float ONE_PLUS_ALPHA = 1.0f + ALPHA;
+
+		if (a_fLinear <= 0.0031308f)
+		{
+			return 12.92f * a_fLinear;
+		}
+		else
+		{
+			return ONE_PLUS_ALPHA * powf(a_fLinear, (1.0f/2.4f)) - ALPHA;
+		}
+	}
+
+	class ColorR8G8B8A8
+	{
+	public:
+
+		unsigned char ucR;
+		unsigned char ucG;
+		unsigned char ucB;
+		unsigned char ucA;
+
+	};
+}

+ 321 - 0
thirdparty/etc2comp/EtcColorFloatRGBA.h

@@ -0,0 +1,321 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "EtcConfig.h"
+#include "EtcColor.h"
+
+#include <math.h>
+
+namespace Etc
+{
+
+	class ColorFloatRGBA
+    {
+    public:
+
+		ColorFloatRGBA(void)
+        {
+            fR = fG = fB = fA = 0.0f;
+        }
+
+		ColorFloatRGBA(float a_fR, float a_fG, float a_fB, float a_fA)
+        {
+            fR = a_fR;
+            fG = a_fG;
+            fB = a_fB;
+            fA = a_fA;
+        }
+
+		inline ColorFloatRGBA operator+(ColorFloatRGBA& a_rfrgba)
+		{
+			ColorFloatRGBA frgba;
+			frgba.fR = fR + a_rfrgba.fR;
+			frgba.fG = fG + a_rfrgba.fG;
+			frgba.fB = fB + a_rfrgba.fB;
+			frgba.fA = fA + a_rfrgba.fA;
+			return frgba;
+		}
+
+		inline ColorFloatRGBA operator+(float a_f)
+		{
+			ColorFloatRGBA frgba;
+			frgba.fR = fR + a_f;
+			frgba.fG = fG + a_f;
+			frgba.fB = fB + a_f;
+			frgba.fA = fA;
+			return frgba;
+		}
+
+		inline ColorFloatRGBA operator-(float a_f)
+		{
+			ColorFloatRGBA frgba;
+			frgba.fR = fR - a_f;
+			frgba.fG = fG - a_f;
+			frgba.fB = fB - a_f;
+			frgba.fA = fA;
+			return frgba;
+		}
+
+		inline ColorFloatRGBA operator-(ColorFloatRGBA& a_rfrgba)
+		{
+			ColorFloatRGBA frgba;
+			frgba.fR = fR - a_rfrgba.fR;
+			frgba.fG = fG - a_rfrgba.fG;
+			frgba.fB = fB - a_rfrgba.fB;
+			frgba.fA = fA - a_rfrgba.fA;
+			return frgba;
+		}
+
+		inline ColorFloatRGBA operator*(float a_f)
+		{
+			ColorFloatRGBA frgba;
+			frgba.fR = fR * a_f;
+			frgba.fG = fG * a_f;
+			frgba.fB = fB * a_f;
+			frgba.fA = fA;
+
+			return frgba;
+		}
+
+		inline ColorFloatRGBA ScaleRGB(float a_f)
+		{
+			ColorFloatRGBA frgba;
+			frgba.fR = a_f * fR;
+			frgba.fG = a_f * fG;
+			frgba.fB = a_f * fB;
+			frgba.fA = fA;
+
+			return frgba;
+		}
+
+		inline ColorFloatRGBA RoundRGB(void)
+		{
+			ColorFloatRGBA frgba;
+			frgba.fR = roundf(fR);
+			frgba.fG = roundf(fG);
+			frgba.fB = roundf(fB);
+
+			return frgba;
+		}
+
+		inline ColorFloatRGBA ToLinear()
+		{
+			ColorFloatRGBA frgbaLinear;
+			frgbaLinear.fR = LogToLinear(fR);
+			frgbaLinear.fG = LogToLinear(fG);
+			frgbaLinear.fB = LogToLinear(fB);
+			frgbaLinear.fA = fA;
+
+			return frgbaLinear;
+		}
+
+		inline ColorFloatRGBA ToLog(void)
+		{
+			ColorFloatRGBA frgbaLog;
+			frgbaLog.fR = LinearToLog(fR);
+			frgbaLog.fG = LinearToLog(fG);
+			frgbaLog.fB = LinearToLog(fB);
+			frgbaLog.fA = fA;
+
+			return frgbaLog;
+		}
+
+		inline static ColorFloatRGBA ConvertFromRGBA8(unsigned char a_ucR, 
+			unsigned char a_ucG, unsigned char a_ucB, unsigned char a_ucA)
+		{
+			ColorFloatRGBA frgba;
+
+			frgba.fR = (float)a_ucR / 255.0f;
+			frgba.fG = (float)a_ucG / 255.0f;
+			frgba.fB = (float)a_ucB / 255.0f;
+			frgba.fA = (float)a_ucA / 255.0f;
+
+			return frgba;
+		}
+
+		inline static ColorFloatRGBA ConvertFromRGB4(unsigned char a_ucR4,
+														unsigned char a_ucG4,
+														unsigned char a_ucB4)
+		{
+			ColorFloatRGBA frgba;
+
+			unsigned char ucR8 = (unsigned char)((a_ucR4 << 4) + a_ucR4);
+			unsigned char ucG8 = (unsigned char)((a_ucG4 << 4) + a_ucG4);
+			unsigned char ucB8 = (unsigned char)((a_ucB4 << 4) + a_ucB4);
+
+			frgba.fR = (float)ucR8 / 255.0f;
+			frgba.fG = (float)ucG8 / 255.0f;
+			frgba.fB = (float)ucB8 / 255.0f;
+			frgba.fA = 1.0f;
+
+			return frgba;
+		}
+
+		inline static ColorFloatRGBA ConvertFromRGB5(unsigned char a_ucR5,
+			unsigned char a_ucG5,
+			unsigned char a_ucB5)
+		{
+			ColorFloatRGBA frgba;
+
+			unsigned char ucR8 = (unsigned char)((a_ucR5 << 3) + (a_ucR5 >> 2));
+			unsigned char ucG8 = (unsigned char)((a_ucG5 << 3) + (a_ucG5 >> 2));
+			unsigned char ucB8 = (unsigned char)((a_ucB5 << 3) + (a_ucB5 >> 2));
+
+			frgba.fR = (float)ucR8 / 255.0f;
+			frgba.fG = (float)ucG8 / 255.0f;
+			frgba.fB = (float)ucB8 / 255.0f;
+			frgba.fA = 1.0f;
+
+			return frgba;
+		}
+
+		inline static ColorFloatRGBA ConvertFromR6G7B6(unsigned char a_ucR6,
+			unsigned char a_ucG7,
+			unsigned char a_ucB6)
+		{
+			ColorFloatRGBA frgba;
+
+			unsigned char ucR8 = (unsigned char)((a_ucR6 << 2) + (a_ucR6 >> 4));
+			unsigned char ucG8 = (unsigned char)((a_ucG7 << 1) + (a_ucG7 >> 6));
+			unsigned char ucB8 = (unsigned char)((a_ucB6 << 2) + (a_ucB6 >> 4));
+
+			frgba.fR = (float)ucR8 / 255.0f;
+			frgba.fG = (float)ucG8 / 255.0f;
+			frgba.fB = (float)ucB8 / 255.0f;
+			frgba.fA = 1.0f;
+
+			return frgba;
+		}
+
+		// quantize to 4 bits, expand to 8 bits
+		inline ColorFloatRGBA QuantizeR4G4B4(void) const
+		{
+			ColorFloatRGBA frgba = *this;
+
+			// quantize to 4 bits
+			frgba = frgba.ClampRGB().ScaleRGB(15.0f).RoundRGB();
+			unsigned int uiR4 = (unsigned int)frgba.fR;
+			unsigned int uiG4 = (unsigned int)frgba.fG;
+			unsigned int uiB4 = (unsigned int)frgba.fB;
+
+			// expand to 8 bits
+			frgba.fR = (float) ((uiR4 << 4) + uiR4);
+			frgba.fG = (float) ((uiG4 << 4) + uiG4);
+			frgba.fB = (float) ((uiB4 << 4) + uiB4);
+
+			frgba = frgba.ScaleRGB(1.0f/255.0f);
+
+			return frgba;
+		}
+
+		// quantize to 5 bits, expand to 8 bits
+		inline ColorFloatRGBA QuantizeR5G5B5(void) const
+		{
+			ColorFloatRGBA frgba = *this;
+
+			// quantize to 5 bits
+			frgba = frgba.ClampRGB().ScaleRGB(31.0f).RoundRGB();
+			unsigned int uiR5 = (unsigned int)frgba.fR;
+			unsigned int uiG5 = (unsigned int)frgba.fG;
+			unsigned int uiB5 = (unsigned int)frgba.fB;
+
+			// expand to 8 bits
+			frgba.fR = (float)((uiR5 << 3) + (uiR5 >> 2));
+			frgba.fG = (float)((uiG5 << 3) + (uiG5 >> 2));
+			frgba.fB = (float)((uiB5 << 3) + (uiB5 >> 2));
+
+			frgba = frgba.ScaleRGB(1.0f / 255.0f);
+
+			return frgba;
+		}
+
+		// quantize to 6/7/6 bits, expand to 8 bits
+		inline ColorFloatRGBA QuantizeR6G7B6(void) const
+		{
+			ColorFloatRGBA frgba = *this;
+
+			// quantize to 6/7/6 bits
+			ColorFloatRGBA frgba6 = frgba.ClampRGB().ScaleRGB(63.0f).RoundRGB();
+			ColorFloatRGBA frgba7 = frgba.ClampRGB().ScaleRGB(127.0f).RoundRGB();
+			unsigned int uiR6 = (unsigned int)frgba6.fR;
+			unsigned int uiG7 = (unsigned int)frgba7.fG;
+			unsigned int uiB6 = (unsigned int)frgba6.fB;
+
+			// expand to 8 bits
+			frgba.fR = (float)((uiR6 << 2) + (uiR6 >> 4));
+			frgba.fG = (float)((uiG7 << 1) + (uiG7 >> 6));
+			frgba.fB = (float)((uiB6 << 2) + (uiB6 >> 4));
+
+			frgba = frgba.ScaleRGB(1.0f / 255.0f);
+
+			return frgba;
+		}
+
+		inline ColorFloatRGBA ClampRGB(void)
+		{
+			ColorFloatRGBA frgba = *this;
+			if (frgba.fR < 0.0f) { frgba.fR = 0.0f; }
+			if (frgba.fR > 1.0f) { frgba.fR = 1.0f; }
+			if (frgba.fG < 0.0f) { frgba.fG = 0.0f; }
+			if (frgba.fG > 1.0f) { frgba.fG = 1.0f; }
+			if (frgba.fB < 0.0f) { frgba.fB = 0.0f; }
+			if (frgba.fB > 1.0f) { frgba.fB = 1.0f; }
+
+			return frgba;
+		}
+
+		inline ColorFloatRGBA ClampRGBA(void)
+		{
+			ColorFloatRGBA frgba = *this;
+			if (frgba.fR < 0.0f) { frgba.fR = 0.0f; }
+			if (frgba.fR > 1.0f) { frgba.fR = 1.0f; }
+			if (frgba.fG < 0.0f) { frgba.fG = 0.0f; }
+			if (frgba.fG > 1.0f) { frgba.fG = 1.0f; }
+			if (frgba.fB < 0.0f) { frgba.fB = 0.0f; }
+			if (frgba.fB > 1.0f) { frgba.fB = 1.0f; }
+			if (frgba.fA < 0.0f) { frgba.fA = 0.0f; }
+			if (frgba.fA > 1.0f) { frgba.fA = 1.0f; }
+
+			return frgba;
+		}
+
+		inline int IntRed(float a_fScale)
+		{
+			return (int)roundf(fR * a_fScale);
+		}
+
+		inline int IntGreen(float a_fScale)
+		{
+			return (int)roundf(fG * a_fScale);
+		}
+
+		inline int IntBlue(float a_fScale)
+		{
+			return (int)roundf(fB * a_fScale);
+		}
+
+		inline int IntAlpha(float a_fScale)
+		{
+			return (int)roundf(fA * a_fScale);
+		}
+
+		float	fR, fG, fB, fA;
+    };
+
+}
+

+ 67 - 0
thirdparty/etc2comp/EtcConfig.h

@@ -0,0 +1,67 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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.
+ */
+
+#pragma once
+
+#ifdef _WIN32
+#define ETC_WINDOWS (1)
+#else
+#define ETC_WINDOWS (0)
+#endif
+
+#if __APPLE__
+#define ETC_OSX (1)
+#else
+#define ETC_OSX (0)
+#endif
+
+#if __unix__
+#define ETC_UNIX (1)
+#else
+#define ETC_UNIX (0)
+#endif
+
+
+// short names for common types
+#include <stdint.h>
+typedef int8_t i8;
+typedef int16_t i16;
+typedef int32_t i32;
+typedef int64_t i64;
+
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+
+typedef float	f32;
+typedef double	f64;
+
+// Keep asserts enabled in release builds during development
+#undef NDEBUG
+
+// 0=disable. stb_image can be used if you need to compress
+//other image formats like jpg
+#define USE_STB_IMAGE_LOAD 0
+
+#if ETC_WINDOWS
+#include <sdkddkver.h>
+#define _CRT_SECURE_NO_WARNINGS (1)
+#include <tchar.h>
+#endif
+
+#include <stdio.h>
+

+ 173 - 0
thirdparty/etc2comp/EtcDifferentialTrys.cpp

@@ -0,0 +1,173 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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.
+ */
+
+/*
+EtcDifferentialTrys.cpp
+
+Gathers the results of the various encoding trys for both halves of a 4x4 block for Differential mode
+
+*/
+
+#include "EtcConfig.h"
+#include "EtcDifferentialTrys.h"
+
+#include <assert.h>
+
+namespace Etc
+{
+
+	// ----------------------------------------------------------------------------------------------------
+	// construct a list of trys (encoding attempts)
+	//
+	// a_frgbaColor1 is the basecolor for the first half
+	// a_frgbaColor2 is the basecolor for the second half
+	// a_pauiPixelMapping1 is the pixel order for the first half
+	// a_pauiPixelMapping2 is the pixel order for the second half
+	// a_uiRadius is the amount to vary the base colors
+	//
+	DifferentialTrys::DifferentialTrys(ColorFloatRGBA a_frgbaColor1, ColorFloatRGBA a_frgbaColor2,
+										const unsigned int *a_pauiPixelMapping1,
+										const unsigned int *a_pauiPixelMapping2,
+										unsigned int a_uiRadius,
+										int a_iGrayOffset1, int a_iGrayOffset2)
+	{
+		assert(a_uiRadius <= MAX_RADIUS);
+
+		m_boolSeverelyBentColors = false;
+
+		ColorFloatRGBA frgbaQuantizedColor1 = a_frgbaColor1.QuantizeR5G5B5();
+		ColorFloatRGBA frgbaQuantizedColor2 = a_frgbaColor2.QuantizeR5G5B5();
+
+		// quantize base colors
+		// ensure that trys with a_uiRadius don't overflow
+		int iRed1 = MoveAwayFromEdge(frgbaQuantizedColor1.IntRed(31.0f)+a_iGrayOffset1, a_uiRadius);
+		int iGreen1 = MoveAwayFromEdge(frgbaQuantizedColor1.IntGreen(31.0f) + a_iGrayOffset1, a_uiRadius);
+		int iBlue1 = MoveAwayFromEdge(frgbaQuantizedColor1.IntBlue(31.0f) + a_iGrayOffset1, a_uiRadius);
+		int iRed2 = MoveAwayFromEdge(frgbaQuantizedColor2.IntRed(31.0f) + a_iGrayOffset2, a_uiRadius);
+		int iGreen2 = MoveAwayFromEdge(frgbaQuantizedColor2.IntGreen(31.0f) + a_iGrayOffset2, a_uiRadius);
+		int iBlue2 = MoveAwayFromEdge(frgbaQuantizedColor2.IntBlue(31.0f) + a_iGrayOffset2, a_uiRadius);
+
+		int iDeltaRed = iRed2 - iRed1;
+		int iDeltaGreen = iGreen2 - iGreen1;
+		int iDeltaBlue = iBlue2 - iBlue1;
+
+		// make sure components are within range
+		{
+			if (iDeltaRed > 3)
+			{
+				if (iDeltaRed > 7)
+				{
+					m_boolSeverelyBentColors = true;
+				}
+
+				iRed1 += (iDeltaRed - 3) / 2;
+				iRed2 = iRed1 + 3;
+				iDeltaRed = 3;
+			}
+			else if (iDeltaRed < -4)
+			{
+				if (iDeltaRed < -8)
+				{
+					m_boolSeverelyBentColors = true;
+				}
+
+				iRed1 += (iDeltaRed + 4) / 2;
+				iRed2 = iRed1 - 4;
+				iDeltaRed = -4;
+			}
+			assert(iRed1 >= (signed)(0 + a_uiRadius) && iRed1 <= (signed)(31 - a_uiRadius));
+			assert(iRed2 >= (signed)(0 + a_uiRadius) && iRed2 <= (signed)(31 - a_uiRadius));
+			assert(iDeltaRed >= -4 && iDeltaRed <= 3);
+
+			if (iDeltaGreen > 3)
+			{
+				if (iDeltaGreen > 7)
+				{
+					m_boolSeverelyBentColors = true;
+				}
+
+				iGreen1 += (iDeltaGreen - 3) / 2;
+				iGreen2 = iGreen1 + 3;
+				iDeltaGreen = 3;
+			}
+			else if (iDeltaGreen < -4)
+			{
+				if (iDeltaGreen < -8)
+				{
+					m_boolSeverelyBentColors = true;
+				}
+
+				iGreen1 += (iDeltaGreen + 4) / 2;
+				iGreen2 = iGreen1 - 4;
+				iDeltaGreen = -4;
+			}
+			assert(iGreen1 >= (signed)(0 + a_uiRadius) && iGreen1 <= (signed)(31 - a_uiRadius));
+			assert(iGreen2 >= (signed)(0 + a_uiRadius) && iGreen2 <= (signed)(31 - a_uiRadius));
+			assert(iDeltaGreen >= -4 && iDeltaGreen <= 3);
+
+			if (iDeltaBlue > 3)
+			{
+				if (iDeltaBlue > 7)
+				{
+					m_boolSeverelyBentColors = true;
+				}
+
+				iBlue1 += (iDeltaBlue - 3) / 2;
+				iBlue2 = iBlue1 + 3;
+				iDeltaBlue = 3;
+			}
+			else if (iDeltaBlue < -4)
+			{
+				if (iDeltaBlue < -8)
+				{
+					m_boolSeverelyBentColors = true;
+				}
+
+				iBlue1 += (iDeltaBlue + 4) / 2;
+				iBlue2 = iBlue1 - 4;
+				iDeltaBlue = -4;
+			}
+			assert(iBlue1 >= (signed)(0+a_uiRadius) && iBlue1 <= (signed)(31 - a_uiRadius));
+			assert(iBlue2 >= (signed)(0 + a_uiRadius) && iBlue2 <= (signed)(31 - a_uiRadius));
+			assert(iDeltaBlue >= -4 && iDeltaBlue <= 3);
+		}
+
+		m_half1.Init(iRed1, iGreen1, iBlue1, a_pauiPixelMapping1, a_uiRadius);
+		m_half2.Init(iRed2, iGreen2, iBlue2, a_pauiPixelMapping2, a_uiRadius);
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+	void DifferentialTrys::Half::Init(int a_iRed, int a_iGreen, int a_iBlue, 
+										const unsigned int *a_pauiPixelMapping, unsigned int a_uiRadius)
+	{
+
+		m_iRed = a_iRed;
+		m_iGreen = a_iGreen;
+		m_iBlue = a_iBlue;
+
+		m_pauiPixelMapping = a_pauiPixelMapping;
+		m_uiRadius = a_uiRadius;
+
+		m_uiTrys = 0;
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+
+} // namespace Etc

+ 97 - 0
thirdparty/etc2comp/EtcDifferentialTrys.h

@@ -0,0 +1,97 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "EtcColorFloatRGBA.h"
+
+namespace Etc
+{
+
+	class DifferentialTrys
+	{
+	public:
+
+		static const unsigned int MAX_RADIUS = 2;
+
+		DifferentialTrys(ColorFloatRGBA a_frgbaColor1,
+							ColorFloatRGBA a_frgbaColor2,
+							const unsigned int *a_pauiPixelMapping1,
+							const unsigned int *a_pauiPixelMapping2,
+							unsigned int a_uiRadius,
+							int a_iGrayOffset1, int a_iGrayOffset2);
+
+		inline static int MoveAwayFromEdge(int a_i, int a_iDistance)
+		{
+			if (a_i < (0+ a_iDistance))
+			{
+				return (0 + a_iDistance);
+			}
+			else if (a_i > (31- a_iDistance))
+			{
+				return (31 - a_iDistance);
+			}
+
+			return a_i;
+		}
+
+		class Try
+		{
+        public :
+			static const unsigned int SELECTORS = 8;	// per half
+
+			int m_iRed;
+			int m_iGreen;
+			int m_iBlue;
+			unsigned int m_uiCW;
+			unsigned int m_auiSelectors[SELECTORS];
+			float m_fError;
+        };
+
+		class Half
+		{
+		public:
+
+			static const unsigned int MAX_TRYS = 125;
+
+			void Init(int a_iRed, int a_iGreen, int a_iBlue, 
+						const unsigned int *a_pauiPixelMapping,
+						unsigned int a_uiRadius);
+
+			// center of trys
+			int m_iRed;
+			int m_iGreen;
+			int m_iBlue;
+
+			const unsigned int *m_pauiPixelMapping;
+			unsigned int m_uiRadius;
+
+			unsigned int m_uiTrys;
+			Try m_atry[MAX_TRYS];
+
+			Try *m_ptryBest;
+		};
+
+		Half m_half1;
+		Half m_half2;
+
+		bool m_boolSeverelyBentColors;
+	};
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+
+} // namespace Etc

+ 54 - 0
thirdparty/etc2comp/EtcErrorMetric.h

@@ -0,0 +1,54 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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.
+ */
+
+#pragma once
+
+namespace Etc
+{
+
+	enum ErrorMetric
+	{
+		RGBA,
+		RGBX,
+		REC709,
+		NUMERIC,
+		NORMALXYZ,
+		//
+		ERROR_METRICS,
+		//
+		BT709 = REC709
+	};
+
+	inline const char *ErrorMetricToString(ErrorMetric errorMetric)
+	{
+		switch (errorMetric)
+		{
+		case RGBA:
+			return "RGBA";
+		case RGBX:
+			return "RGBX";
+		case REC709:
+			return "REC709";
+		case NUMERIC:
+			return "NUMERIC";
+		case NORMALXYZ:
+			return "NORMALXYZ";
+		case ERROR_METRICS:
+		default:
+			return "UNKNOWN";
+		}
+	}
+} // namespace Etc

+ 390 - 0
thirdparty/etc2comp/EtcFile.cpp

@@ -0,0 +1,390 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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.
+ */
+
+#ifdef _WIN32
+#define _CRT_SECURE_NO_WARNINGS (1)
+#endif
+
+#include "EtcConfig.h"
+
+
+#include "EtcFile.h"
+
+#include "EtcFileHeader.h"
+#include "EtcColor.h"
+#include "Etc.h"
+#include "EtcBlock4x4EncodingBits.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+
+using namespace Etc;
+
+// ----------------------------------------------------------------------------------------------------
+//
+File::File(const char *a_pstrFilename, Format a_fileformat, Image::Format a_imageformat,
+			unsigned char *a_paucEncodingBits, unsigned int a_uiEncodingBitsBytes,
+			unsigned int a_uiSourceWidth, unsigned int a_uiSourceHeight,
+			unsigned int a_uiExtendedWidth, unsigned int a_uiExtendedHeight)
+{
+	if (a_pstrFilename == nullptr)
+	{
+		m_pstrFilename = const_cast<char *>("");
+	}
+	else
+	{
+		m_pstrFilename = new char[strlen(a_pstrFilename) + 1];
+		strcpy(m_pstrFilename, a_pstrFilename);
+	}
+
+	m_fileformat = a_fileformat;
+	if (m_fileformat == Format::INFER_FROM_FILE_EXTENSION)
+	{
+		// ***** TODO: add this later *****
+		m_fileformat = Format::KTX;
+	}
+
+	m_imageformat = a_imageformat;
+
+	m_uiNumMipmaps = 1;
+	m_pMipmapImages = new RawImage[m_uiNumMipmaps];
+	m_pMipmapImages[0].paucEncodingBits = std::shared_ptr<unsigned char>(a_paucEncodingBits, [](unsigned char *p) { delete[] p; } );
+	m_pMipmapImages[0].uiEncodingBitsBytes = a_uiEncodingBitsBytes;
+	m_pMipmapImages[0].uiExtendedWidth = a_uiExtendedWidth;
+	m_pMipmapImages[0].uiExtendedHeight = a_uiExtendedHeight;
+
+	m_uiSourceWidth = a_uiSourceWidth;
+	m_uiSourceHeight = a_uiSourceHeight;
+
+	switch (m_fileformat)
+	{
+	case Format::PKM:
+		m_pheader = new FileHeader_Pkm(this);
+		break;
+
+	case Format::KTX:
+		m_pheader = new FileHeader_Ktx(this);
+		break;
+
+	default:
+		assert(0);
+		break;
+	}
+
+}
+
+// ----------------------------------------------------------------------------------------------------
+//
+File::File(const char *a_pstrFilename, Format a_fileformat, Image::Format a_imageformat,
+	unsigned int a_uiNumMipmaps, RawImage *a_pMipmapImages,
+	unsigned int a_uiSourceWidth, unsigned int a_uiSourceHeight)
+{
+	if (a_pstrFilename == nullptr)
+	{
+		m_pstrFilename = const_cast<char *>("");
+	}
+	else
+	{
+		m_pstrFilename = new char[strlen(a_pstrFilename) + 1];
+		strcpy(m_pstrFilename, a_pstrFilename);
+	}
+
+	m_fileformat = a_fileformat;
+	if (m_fileformat == Format::INFER_FROM_FILE_EXTENSION)
+	{
+		// ***** TODO: add this later *****
+		m_fileformat = Format::KTX;
+	}
+
+	m_imageformat = a_imageformat;
+
+	m_uiNumMipmaps = a_uiNumMipmaps;
+	m_pMipmapImages = new RawImage[m_uiNumMipmaps];
+
+	for(unsigned int mip = 0; mip < m_uiNumMipmaps; mip++)
+	{
+		m_pMipmapImages[mip] = a_pMipmapImages[mip];
+	}
+
+	m_uiSourceWidth = a_uiSourceWidth;
+	m_uiSourceHeight = a_uiSourceHeight;
+
+	switch (m_fileformat)
+	{
+	case Format::PKM:
+		m_pheader = new FileHeader_Pkm(this);
+		break;
+
+	case Format::KTX:
+		m_pheader = new FileHeader_Ktx(this);
+		break;
+
+	default:
+		assert(0);
+		break;
+	}
+
+}
+
+// ----------------------------------------------------------------------------------------------------
+//
+File::File(const char *a_pstrFilename, Format a_fileformat)
+{
+	if (a_pstrFilename == nullptr)
+	{
+		return;
+	}
+	else
+	{
+		m_pstrFilename = new char[strlen(a_pstrFilename) + 1];
+		strcpy(m_pstrFilename, a_pstrFilename);
+	}
+
+	m_fileformat = a_fileformat;
+	if (m_fileformat == Format::INFER_FROM_FILE_EXTENSION)
+	{
+		// ***** TODO: add this later *****
+		m_fileformat = Format::KTX;
+	}
+
+	FILE *pfile = fopen(m_pstrFilename, "rb");
+	if (pfile == nullptr)
+	{
+		printf("ERROR: Couldn't open %s", m_pstrFilename);
+		exit(1);
+	}
+	fseek(pfile, 0, SEEK_END);
+	unsigned int fileSize = ftell(pfile);
+	fseek(pfile, 0, SEEK_SET);
+	size_t szResult;
+
+	m_pheader = new FileHeader_Ktx(this);
+	szResult = fread( ((FileHeader_Ktx*)m_pheader)->GetData(), 1, sizeof(FileHeader_Ktx::Data), pfile);
+	assert(szResult > 0);
+
+	m_uiNumMipmaps = 1;
+	m_pMipmapImages = new RawImage[m_uiNumMipmaps];
+
+	if (((FileHeader_Ktx*)m_pheader)->GetData()->m_u32BytesOfKeyValueData > 0)
+		fseek(pfile, ((FileHeader_Ktx*)m_pheader)->GetData()->m_u32BytesOfKeyValueData, SEEK_CUR);
+	szResult = fread(&m_pMipmapImages->uiEncodingBitsBytes, 1, sizeof(unsigned int), pfile);
+	assert(szResult > 0);
+
+	m_pMipmapImages->paucEncodingBits = std::shared_ptr<unsigned char>(new unsigned char[m_pMipmapImages->uiEncodingBitsBytes], [](unsigned char *p) { delete[] p; } );
+	assert(ftell(pfile) + m_pMipmapImages->uiEncodingBitsBytes <= fileSize);
+	szResult = fread(m_pMipmapImages->paucEncodingBits.get(), 1, m_pMipmapImages->uiEncodingBitsBytes, pfile);
+	assert(szResult == m_pMipmapImages->uiEncodingBitsBytes);
+
+	uint32_t uiInternalFormat = ((FileHeader_Ktx*)m_pheader)->GetData()->m_u32GlInternalFormat;
+	uint32_t uiBaseInternalFormat = ((FileHeader_Ktx*)m_pheader)->GetData()->m_u32GlBaseInternalFormat;
+	
+	if (uiInternalFormat == (uint32_t)FileHeader_Ktx::InternalFormat::ETC1_RGB8 && uiBaseInternalFormat == (uint32_t)FileHeader_Ktx::BaseInternalFormat::ETC1_RGB8)
+	{
+		m_imageformat = Image::Format::ETC1;
+	}
+	else if (uiInternalFormat == (uint32_t)FileHeader_Ktx::InternalFormat::ETC2_RGB8 && uiBaseInternalFormat == (uint32_t)FileHeader_Ktx::BaseInternalFormat::ETC2_RGB8)
+	{
+		m_imageformat = Image::Format::RGB8;
+	}
+	else if (uiInternalFormat == (uint32_t)FileHeader_Ktx::InternalFormat::ETC2_RGB8A1 && uiBaseInternalFormat == (uint32_t)FileHeader_Ktx::BaseInternalFormat::ETC2_RGB8A1)
+	{
+		m_imageformat = Image::Format::RGB8A1;
+	}
+	else if (uiInternalFormat == (uint32_t)FileHeader_Ktx::InternalFormat::ETC2_RGBA8 && uiBaseInternalFormat == (uint32_t)FileHeader_Ktx::BaseInternalFormat::ETC2_RGBA8)
+	{
+		m_imageformat = Image::Format::RGBA8;
+	}
+	else if (uiInternalFormat == (uint32_t)FileHeader_Ktx::InternalFormat::ETC2_R11 && uiBaseInternalFormat == (uint32_t)FileHeader_Ktx::BaseInternalFormat::ETC2_R11)
+	{
+		m_imageformat = Image::Format::R11;
+	}
+	else if (uiInternalFormat == (uint32_t)FileHeader_Ktx::InternalFormat::ETC2_SIGNED_R11 && uiBaseInternalFormat == (uint32_t)FileHeader_Ktx::BaseInternalFormat::ETC2_R11)
+	{
+		m_imageformat = Image::Format::SIGNED_R11;
+	}
+	else if (uiInternalFormat == (uint32_t)FileHeader_Ktx::InternalFormat::ETC2_RG11 && uiBaseInternalFormat == (uint32_t)FileHeader_Ktx::BaseInternalFormat::ETC2_RG11)
+	{
+		m_imageformat = Image::Format::RG11;
+	}
+	else if (uiInternalFormat == (uint32_t)FileHeader_Ktx::InternalFormat::ETC2_SIGNED_RG11 && uiBaseInternalFormat == (uint32_t)FileHeader_Ktx::BaseInternalFormat::ETC2_RG11)
+	{
+		m_imageformat = Image::Format::SIGNED_RG11;
+	}
+	else
+	{
+		m_imageformat = Image::Format::UNKNOWN;
+	}
+
+	m_uiSourceWidth = ((FileHeader_Ktx*)m_pheader)->GetData()->m_u32PixelWidth;
+	m_uiSourceHeight = ((FileHeader_Ktx*)m_pheader)->GetData()->m_u32PixelHeight;
+	m_pMipmapImages->uiExtendedWidth = Image::CalcExtendedDimension((unsigned short)m_uiSourceWidth);
+	m_pMipmapImages->uiExtendedHeight = Image::CalcExtendedDimension((unsigned short)m_uiSourceHeight);
+
+	unsigned int uiBlocks = m_pMipmapImages->uiExtendedWidth * m_pMipmapImages->uiExtendedHeight / 16;
+	Block4x4EncodingBits::Format encodingbitsformat = Image::DetermineEncodingBitsFormat(m_imageformat);
+	unsigned int expectedbytes = uiBlocks * Block4x4EncodingBits::GetBytesPerBlock(encodingbitsformat);
+	assert(expectedbytes == m_pMipmapImages->uiEncodingBitsBytes);
+
+	fclose(pfile);
+}
+
+File::~File()
+{
+	if (m_pMipmapImages != nullptr)
+	{
+		delete [] m_pMipmapImages;
+	}
+
+	if(m_pstrFilename != nullptr)
+	{
+		delete[] m_pstrFilename;
+		m_pstrFilename = nullptr;
+	}
+	if (m_pheader != nullptr)
+	{
+		delete m_pheader;
+		m_pheader = nullptr;
+	}
+}
+
+void File::UseSingleBlock(int a_iPixelX, int a_iPixelY)
+{
+	if (a_iPixelX <= -1 || a_iPixelY <= -1)
+		return;
+	if (a_iPixelX >(int) m_uiSourceWidth)
+	{
+		//if we are using a ktx thats the size of a single block or less
+		//then make sure we use the 4x4 image as the single block
+		if (m_uiSourceWidth <= 4)
+		{
+			a_iPixelX = 0;
+		}
+		else
+		{
+			printf("blockAtHV: H coordinate out of range, capped to image width\n");
+			a_iPixelX = m_uiSourceWidth - 1;
+		}
+	}
+	if (a_iPixelY >(int) m_uiSourceHeight)
+	{
+		//if we are using a ktx thats the size of a single block or less
+		//then make sure we use the 4x4 image as the single block
+		if (m_uiSourceHeight <= 4)
+		{
+			a_iPixelY= 0;
+		}
+		else
+		{
+			printf("blockAtHV: V coordinate out of range, capped to image height\n");
+			a_iPixelY = m_uiSourceHeight - 1;
+		}
+	}
+
+	unsigned int origWidth = m_uiSourceWidth;
+	unsigned int origHeight = m_uiSourceHeight;
+
+	m_uiSourceWidth = 4;
+	m_uiSourceHeight = 4;
+
+	Block4x4EncodingBits::Format encodingbitsformat = Image::DetermineEncodingBitsFormat(m_imageformat);
+	unsigned int uiEncodingBitsBytesPerBlock = Block4x4EncodingBits::GetBytesPerBlock(encodingbitsformat);
+
+	int numMipmaps = 1;
+	RawImage* pMipmapImages = new RawImage[numMipmaps];
+	pMipmapImages[0].uiExtendedWidth = Image::CalcExtendedDimension((unsigned short)m_uiSourceWidth);
+	pMipmapImages[0].uiExtendedHeight = Image::CalcExtendedDimension((unsigned short)m_uiSourceHeight);
+	pMipmapImages[0].uiEncodingBitsBytes = 0;
+	pMipmapImages[0].paucEncodingBits = std::shared_ptr<unsigned char>(new unsigned char[uiEncodingBitsBytesPerBlock], [](unsigned char *p) { delete[] p; });
+
+	//block position in pixels
+	// remove the bottom 2 bits to get the block coordinates 
+	unsigned int iBlockPosX = (a_iPixelX & 0xFFFFFFFC);
+	unsigned int iBlockPosY = (a_iPixelY & 0xFFFFFFFC);
+
+	int numXBlocks = (origWidth / 4);
+	int numYBlocks = (origHeight / 4);
+	
+
+	// block location 
+	//int iBlockX = (a_iPixelX % 4) == 0 ? a_iPixelX / 4.0f : (a_iPixelX / 4) + 1;
+	//int iBlockY = (a_iPixelY % 4) == 0 ? a_iPixelY / 4.0f : (a_iPixelY / 4) + 1;
+	//m_paucEncodingBits += ((iBlockY * numXBlocks) + iBlockX) * uiEncodingBitsBytesPerBlock;
+
+	
+	unsigned int num = numXBlocks*numYBlocks;
+	unsigned int uiH = 0, uiV = 0;
+	unsigned char* pEncodingBits = m_pMipmapImages[0].paucEncodingBits.get();
+	for (unsigned int uiBlock = 0; uiBlock < num; uiBlock++)
+	{
+		if (uiH == iBlockPosX && uiV == iBlockPosY)
+		{
+			memcpy(pMipmapImages[0].paucEncodingBits.get(),pEncodingBits, uiEncodingBitsBytesPerBlock);
+			break;
+		}
+		pEncodingBits += uiEncodingBitsBytesPerBlock;
+		uiH += 4;
+
+		if (uiH >= origWidth)
+		{
+			uiH = 0;
+			uiV += 4;
+		}
+	}
+
+	delete [] m_pMipmapImages;
+	m_pMipmapImages = pMipmapImages;
+}
+// ----------------------------------------------------------------------------------------------------
+//
+void File::Write()
+{
+
+	FILE *pfile = fopen(m_pstrFilename, "wb");
+	if (pfile == nullptr)
+	{
+		printf("Error: couldn't open Etc file (%s)\n", m_pstrFilename);
+		exit(1);
+	}
+
+	m_pheader->Write(pfile);
+
+	for(unsigned int mip = 0; mip < m_uiNumMipmaps; mip++)
+	{
+		if(m_fileformat == Format::KTX)
+		{
+			// Write u32 image size
+			uint32_t u32ImageSize = m_pMipmapImages[mip].uiEncodingBitsBytes;
+			uint32_t szBytesWritten = fwrite(&u32ImageSize, 1, sizeof(u32ImageSize), pfile);
+			assert(szBytesWritten == sizeof(u32ImageSize));
+		}
+
+		unsigned int iResult = (int)fwrite(m_pMipmapImages[mip].paucEncodingBits.get(), 1, m_pMipmapImages[mip].uiEncodingBitsBytes, pfile);
+		if (iResult != m_pMipmapImages[mip].uiEncodingBitsBytes)
+	{
+		printf("Error: couldn't write Etc file (%s)\n", m_pstrFilename);
+		exit(1);
+		}
+	}
+
+	fclose(pfile);
+
+}
+
+// ----------------------------------------------------------------------------------------------------
+//
+

+ 136 - 0
thirdparty/etc2comp/EtcFile.h

@@ -0,0 +1,136 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "EtcColorFloatRGBA.h"
+#include "EtcImage.h"
+#include "Etc.h"
+
+namespace Etc
+{
+	class FileHeader;
+	class SourceImage;
+
+	class File
+	{
+	public:
+
+		enum class Format
+		{
+			INFER_FROM_FILE_EXTENSION,
+			PKM,
+			KTX,
+		};
+
+		File(const char *a_pstrFilename, Format a_fileformat, Image::Format a_imageformat,
+				unsigned char *a_paucEncodingBits, unsigned int a_uiEncodingBitsBytes,
+				unsigned int a_uiSourceWidth, unsigned int a_uiSourceHeight,
+				unsigned int a_uiExtendedWidth, unsigned int a_uiExtendedHeight);
+
+		File(const char *a_pstrFilename, Format a_fileformat, Image::Format a_imageformat,
+			unsigned int a_uiNumMipmaps, RawImage *pMipmapImages,
+			unsigned int a_uiSourceWidth, unsigned int a_uiSourceHeight );
+
+		File(const char *a_pstrFilename, Format a_fileformat);
+		~File();
+		const char *GetFilename(void) { return m_pstrFilename; }
+
+		void Read(const char *a_pstrFilename);
+		void Write(void);
+
+		inline unsigned int GetSourceWidth(void)
+		{
+			return m_uiSourceWidth;
+		}
+
+		inline unsigned int GetSourceHeight(void)
+		{
+			return m_uiSourceHeight;
+		}
+
+		inline unsigned int GetExtendedWidth(unsigned int mipmapIndex = 0)
+		{
+			if (mipmapIndex < m_uiNumMipmaps)
+			{
+				return m_pMipmapImages[mipmapIndex].uiExtendedWidth;
+			}
+			else
+			{
+				return 0;
+			}
+		}
+
+		inline unsigned int GetExtendedHeight(unsigned int mipmapIndex = 0)
+		{
+			if (mipmapIndex < m_uiNumMipmaps)
+			{
+				return m_pMipmapImages[mipmapIndex].uiExtendedHeight;
+			}
+			else
+			{
+				return 0;
+			}
+		}
+
+		inline Image::Format GetImageFormat()
+		{
+			return m_imageformat;
+		}
+
+		inline unsigned int GetEncodingBitsBytes(unsigned int mipmapIndex = 0)
+		{
+			if (mipmapIndex < m_uiNumMipmaps)
+			{
+				return m_pMipmapImages[mipmapIndex].uiEncodingBitsBytes;
+			}
+			else
+			{
+				return 0;
+			}
+		}
+
+		inline unsigned char*  GetEncodingBits(unsigned int mipmapIndex = 0)
+		{
+			if( mipmapIndex < m_uiNumMipmaps)
+			{
+				return m_pMipmapImages[mipmapIndex].paucEncodingBits.get();
+			}
+			else
+			{
+				return nullptr;
+			}
+		}
+
+		inline unsigned int GetNumMipmaps() 
+		{
+			return m_uiNumMipmaps; 
+		}
+
+		void UseSingleBlock(int a_iPixelX = -1, int a_iPixelY = -1);
+	private:
+
+		char *m_pstrFilename;               // includes directory path and file extension
+		Format m_fileformat;
+		Image::Format m_imageformat;
+		FileHeader *m_pheader;
+		unsigned int m_uiNumMipmaps;
+		RawImage*	 m_pMipmapImages;
+		unsigned int m_uiSourceWidth;
+		unsigned int m_uiSourceHeight;
+	};
+
+}

+ 185 - 0
thirdparty/etc2comp/EtcFileHeader.cpp

@@ -0,0 +1,185 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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 "EtcFileHeader.h"
+
+#include "EtcBlock4x4EncodingBits.h"
+
+#include <assert.h>
+
+namespace Etc
+{
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+	FileHeader_Pkm::FileHeader_Pkm(File *a_pfile)
+	{
+		m_pfile = a_pfile;
+
+		static const char s_acMagicNumberData[4] = { 'P', 'K', 'M', ' ' };
+		static const char s_acVersionData[2] = { '1', '0' };
+
+		for (unsigned int ui = 0; ui < sizeof(s_acMagicNumberData); ui++)
+		{
+			m_data.m_acMagicNumber[ui] = s_acMagicNumberData[ui];
+		}
+
+		for (unsigned int ui = 0; ui < sizeof(s_acVersionData); ui++)
+		{
+			m_data.m_acVersion[ui] = s_acVersionData[ui];
+		}
+
+		m_data.m_ucDataType_msb = 0;        // ETC1_RGB_NO_MIPMAPS
+		m_data.m_ucDataType_lsb = 0;
+
+		m_data.m_ucOriginalWidth_msb = (unsigned char)(m_pfile->GetSourceWidth() >> 8);
+		m_data.m_ucOriginalWidth_lsb = m_pfile->GetSourceWidth() & 0xFF;
+		m_data.m_ucOriginalHeight_msb = (unsigned char)(m_pfile->GetSourceHeight() >> 8);
+		m_data.m_ucOriginalHeight_lsb = m_pfile->GetSourceHeight() & 0xFF;
+
+		m_data.m_ucExtendedWidth_msb = (unsigned char)(m_pfile->GetExtendedWidth() >> 8);
+		m_data.m_ucExtendedWidth_lsb = m_pfile->GetExtendedWidth() & 0xFF;
+		m_data.m_ucExtendedHeight_msb = (unsigned char)(m_pfile->GetExtendedHeight() >> 8);
+		m_data.m_ucExtendedHeight_lsb = m_pfile->GetExtendedHeight() & 0xFF;
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+	void FileHeader_Pkm::Write(FILE *a_pfile)
+	{
+
+		fwrite(&m_data, sizeof(Data), 1, a_pfile);
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+	FileHeader_Ktx::FileHeader_Ktx(File *a_pfile)
+	{
+		m_pfile = a_pfile;
+
+		static const uint8_t s_au8Itentfier[12] =
+		{ 
+			0xAB, 0x4B, 0x54, 0x58, // first four bytes of Byte[12] identifier
+			0x20, 0x31, 0x31, 0xBB, // next four bytes of Byte[12] identifier
+			0x0D, 0x0A, 0x1A, 0x0A  // final four bytes of Byte[12] identifier
+		};
+
+		for (unsigned int ui = 0; ui < sizeof(s_au8Itentfier); ui++)
+		{
+			m_data.m_au8Identifier[ui] = s_au8Itentfier[ui];
+		}
+
+		m_data.m_u32Endianness				= 0x04030201;
+		m_data.m_u32GlType					= 0;
+		m_data.m_u32GlTypeSize				= 1;
+		m_data.m_u32GlFormat				= 0;
+
+		switch (m_pfile->GetImageFormat())
+		{
+		case Image::Format::RGB8:
+		case Image::Format::SRGB8:
+			m_data.m_u32GlInternalFormat = (unsigned int)InternalFormat::ETC2_RGB8;
+			m_data.m_u32GlBaseInternalFormat = (unsigned int)BaseInternalFormat::ETC2_RGB8;
+			break;
+
+		case Image::Format::RGBA8:
+		case Image::Format::SRGBA8:
+			m_data.m_u32GlInternalFormat = (unsigned int)InternalFormat::ETC2_RGBA8;
+			m_data.m_u32GlBaseInternalFormat = (unsigned int)BaseInternalFormat::ETC2_RGBA8;
+			break;
+
+		case Image::Format::RGB8A1:
+		case Image::Format::SRGB8A1:
+			m_data.m_u32GlInternalFormat = (unsigned int)InternalFormat::ETC2_RGB8A1;
+			m_data.m_u32GlBaseInternalFormat = (unsigned int)BaseInternalFormat::ETC2_RGB8A1;
+			break;
+		
+		case Image::Format::R11:
+			m_data.m_u32GlInternalFormat = (unsigned int)InternalFormat::ETC2_R11;
+			m_data.m_u32GlBaseInternalFormat = (unsigned int)BaseInternalFormat::ETC2_R11;
+			break;
+
+		case Image::Format::SIGNED_R11:
+			m_data.m_u32GlInternalFormat = (unsigned int)InternalFormat::ETC2_SIGNED_R11;
+			m_data.m_u32GlBaseInternalFormat = (unsigned int)BaseInternalFormat::ETC2_R11;
+			break;
+		
+		case Image::Format::RG11:
+			m_data.m_u32GlInternalFormat = (unsigned int)InternalFormat::ETC2_RG11;
+			m_data.m_u32GlBaseInternalFormat = (unsigned int)BaseInternalFormat::ETC2_RG11;
+			break;
+
+		case Image::Format::SIGNED_RG11:
+			m_data.m_u32GlInternalFormat = (unsigned int)InternalFormat::ETC2_SIGNED_RG11;
+			m_data.m_u32GlBaseInternalFormat = (unsigned int)BaseInternalFormat::ETC2_RG11;
+			break;
+
+		default:
+			m_data.m_u32GlInternalFormat = (unsigned int)InternalFormat::ETC1_RGB8;
+			m_data.m_u32GlBaseInternalFormat = (unsigned int)BaseInternalFormat::ETC1_RGB8;
+			break;
+		}
+
+		m_data.m_u32PixelWidth				= 0;
+		m_data.m_u32PixelHeight				= 0;
+		m_data.m_u32PixelDepth				= 0;
+		m_data.m_u32NumberOfArrayElements	= 0;
+		m_data.m_u32NumberOfFaces			= 0;
+		m_data.m_u32BytesOfKeyValueData		= 0;
+
+		m_pkeyvaluepair = nullptr;
+
+		m_u32Images = 0;
+		m_u32KeyValuePairs = 0;
+
+		m_data.m_u32PixelWidth = m_pfile->GetSourceWidth();
+		m_data.m_u32PixelHeight = m_pfile->GetSourceHeight();
+		m_data.m_u32PixelDepth = 0;
+		m_data.m_u32NumberOfArrayElements = 0;
+		m_data.m_u32NumberOfFaces = 1;
+		m_data.m_u32NumberOfMipmapLevels = m_pfile->GetNumMipmaps();
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+	void FileHeader_Ktx::Write(FILE *a_pfile)
+	{
+		size_t szBytesWritten;
+
+		// Write header
+		szBytesWritten = fwrite(&m_data, 1, sizeof(Data), a_pfile);
+		assert(szBytesWritten == sizeof(Data));
+
+		// Write KeyAndValuePairs
+		if (m_u32KeyValuePairs)
+		{
+			fwrite(m_pkeyvaluepair, m_pkeyvaluepair->u32KeyAndValueByteSize, 1, a_pfile);
+		}
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+	FileHeader_Ktx::Data *FileHeader_Ktx::GetData()
+	{
+		return &m_data;
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+} // namespace Etc

+ 146 - 0
thirdparty/etc2comp/EtcFileHeader.h

@@ -0,0 +1,146 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "EtcFile.h"
+#include <stdio.h>
+#include <inttypes.h>
+
+namespace Etc
+{
+
+	class Image;
+
+	class FileHeader
+	{
+	public:
+
+		virtual void Write(FILE *a_pfile) = 0;
+		File GetFile();
+		virtual ~FileHeader(void) {}
+	protected:
+
+		File *m_pfile;
+	};
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+    class FileHeader_Pkm : public FileHeader
+    {
+    public:
+
+		FileHeader_Pkm(File *a_pfile);
+
+		virtual void Write(FILE *a_pfile);
+		virtual ~FileHeader_Pkm(void) {}
+	private:
+
+		typedef struct
+		{
+			char m_acMagicNumber[4];
+			char m_acVersion[2];
+			unsigned char m_ucDataType_msb;             // e.g. ETC1_RGB_NO_MIPMAPS
+			unsigned char m_ucDataType_lsb;
+			unsigned char m_ucExtendedWidth_msb;     //  padded to 4x4 blocks
+			unsigned char m_ucExtendedWidth_lsb;
+			unsigned char m_ucExtendedHeight_msb;    //  padded to 4x4 blocks
+			unsigned char m_ucExtendedHeight_lsb;
+			unsigned char m_ucOriginalWidth_msb;
+			unsigned char m_ucOriginalWidth_lsb;
+			unsigned char m_ucOriginalHeight_msb;
+			unsigned char m_ucOriginalHeight_lsb;
+		} Data;
+
+		Data m_data;
+	};
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+    class FileHeader_Ktx : public FileHeader
+    {
+    public:
+
+		typedef struct
+		{
+			uint32_t	u32KeyAndValueByteSize;
+		} KeyValuePair;
+
+		typedef struct
+		{
+			uint8_t m_au8Identifier[12];
+			uint32_t m_u32Endianness;
+			uint32_t m_u32GlType;
+			uint32_t m_u32GlTypeSize;
+			uint32_t m_u32GlFormat;
+			uint32_t m_u32GlInternalFormat;
+			uint32_t m_u32GlBaseInternalFormat;
+			uint32_t m_u32PixelWidth;
+			uint32_t m_u32PixelHeight;
+			uint32_t m_u32PixelDepth;
+			uint32_t m_u32NumberOfArrayElements;
+			uint32_t m_u32NumberOfFaces;
+			uint32_t m_u32NumberOfMipmapLevels;
+			uint32_t m_u32BytesOfKeyValueData;
+		} Data;
+
+		enum class InternalFormat
+		{
+			ETC1_RGB8 = 0x8D64,
+			ETC1_ALPHA8 = ETC1_RGB8,
+			//
+			ETC2_R11 = 0x9270,
+			ETC2_SIGNED_R11 = 0x9271,
+			ETC2_RG11 = 0x9272,
+			ETC2_SIGNED_RG11 = 0x9273,
+			ETC2_RGB8 = 0x9274,
+			ETC2_SRGB8 = 0x9275,
+			ETC2_RGB8A1 = 0x9276,
+			ETC2_SRGB8_PUNCHTHROUGH_ALPHA1 = 0x9277,
+			ETC2_RGBA8 = 0x9278
+		};
+
+		enum class BaseInternalFormat
+		{
+			ETC2_R11 = 0x1903,
+			ETC2_RG11 = 0x8227,
+			ETC1_RGB8 = 0x1907,
+			ETC1_ALPHA8 = ETC1_RGB8,
+			//
+			ETC2_RGB8 = 0x1907,
+			ETC2_RGB8A1 = 0x1908,
+			ETC2_RGBA8 = 0x1908,
+		};
+
+		FileHeader_Ktx(File *a_pfile);
+
+		virtual void Write(FILE *a_pfile);
+		virtual ~FileHeader_Ktx(void) {}
+
+		void AddKeyAndValue(KeyValuePair *a_pkeyvaluepair);
+
+		Data* GetData();
+
+	private:
+
+		Data m_data;
+		KeyValuePair *m_pkeyvaluepair;
+		
+		uint32_t m_u32Images;
+		uint32_t m_u32KeyValuePairs;
+	};
+
+} // namespace Etc

+ 401 - 0
thirdparty/etc2comp/EtcFilter.cpp

@@ -0,0 +1,401 @@
+#include <stdlib.h>
+#include <math.h>
+#include "EtcFilter.h"
+
+
+namespace Etc
+{
+
+static const double PiConst = 3.14159265358979323846;
+
+inline double sinc(double x) 
+{
+    if ( x == 0.0 ) 
+    {
+        return 1.0;
+    }
+
+    return sin(PiConst * x) / (PiConst * x);
+}
+
+//inline float sincf( float x )
+//{
+//    x *= F_PI;
+//    if (x < 0.01f && x > -0.01f)
+//    {
+//        return 1.0f + x*x*(-1.0f/6.0f + x*x*1.0f/120.0f);
+//    }
+//
+//    return sinf(x)/x;
+//}
+//
+//double bessel0(double x) 
+//{
+//    const double EPSILON_RATIO = 1E-16;
+//    double xh, sum, pow, ds;
+//    int k;
+//
+//    xh = 0.5 * x;
+//    sum = 1.0;
+//    pow = 1.0;
+//    k = 0;
+//    ds = 1.0;
+//    while (ds > sum * EPSILON_RATIO) 
+//    {
+//        ++k;
+//        pow = pow * (xh / k);
+//        ds = pow * pow;
+//        sum = sum + ds;
+//    }
+//
+//    return sum;
+//}
+
+//**--------------------------------------------------------------------------
+//** Name: kaiser(double alpha, double half_width, double x) 
+//** Returns:
+//** Description: Alpha controls shape of filter.  We are using 4.
+//**--------------------------------------------------------------------------
+//inline double kaiser(double alpha, double half_width, double x) 
+//{
+//    double ratio = (x / half_width);
+//    return bessel0(alpha * sqrt(1 - ratio * ratio)) / bessel0(alpha);
+//}
+//
+//float Filter_Lanczos4Sinc(float x)
+//{
+//    if (x <= -4.0f || x >= 4.0f)    // half-width of 4
+//    {
+//        return 0.0;
+//    }
+//
+//    return sinc(0.875f * x) * sinc(0.25f * x);
+//}
+//
+//double Filter_Kaiser4( double t )
+//{
+//    return kaiser( 4.0, 3.0, t);
+//}
+//
+//double Filter_KaiserOptimal( double t )
+//{
+//    return kaiser( 8.93, 3.0f, t);
+//}                  
+
+double FilterLanczos3( double t )
+{
+	if ( t <= -3.0 || t >= 3.0 ) 
+    {
+        return 0.0;
+    }
+
+    return sinc( t ) * sinc( t / 3.0 );
+}
+
+double FilterBox( double t )
+{
+    return ( t > -0.5 && t < 0.5) ? 1.0 : 0.0;
+}
+
+double FilterLinear( double t )
+{
+	if (t < 0.0) t = -t;
+
+    return (t < 1.0) ? (1.0 - t) : 0.0;
+}
+
+
+//**--------------------------------------------------------------------------
+//** Name: CalcContributions( int srcSize, 
+//**                          int destSize, 
+//**                          double filterSize, 
+//**						  bool wrap,
+//**                          double (*FilterProc)(double), 
+//**                          FilterWeights contrib[] )
+//** Returns: void
+//** Description:
+//**--------------------------------------------------------------------------
+void CalcContributions( int srcSize, int destSize, double filterSize, bool wrap, double (*FilterProc)(double), FilterWeights contrib[] )
+{
+    double scale;
+    double filterScale;
+    double center;
+    double totalWeight;
+    double weight;
+    int   iRight;
+    int   iLeft;
+    int   iDest;
+
+    scale = (double)destSize / srcSize;
+    if ( scale < 1.0 )
+    {
+        filterSize = filterSize / scale;
+        filterScale = scale;
+    }
+    else
+    {
+        filterScale = 1.0;
+    }
+
+    if ( filterSize > (double)MaxFilterSize )
+    {
+        filterSize = (double)MaxFilterSize;
+    }
+
+    for ( iDest = 0; iDest < destSize; ++iDest )
+    {
+        center = (double)iDest / scale;
+
+        iLeft = (int)ceil(center - filterSize);
+		iRight = (int)floor(center + filterSize);
+
+		if ( !wrap )
+		{
+        if ( iLeft < 0 )
+        {
+            iLeft = 0;
+        }
+
+        if ( iRight >= srcSize )
+        {
+            iRight = srcSize - 1;
+        }
+		}
+
+		int numWeights = iRight - iLeft + 1;
+
+        contrib[iDest].first = iLeft;
+        contrib[iDest].numWeights = numWeights;
+
+        totalWeight = 0;
+		double t = ((double)iLeft - center) * filterScale;
+		for (int i = 0; i < numWeights; i++)
+        {
+			weight = (*FilterProc)(t) * filterScale;
+            totalWeight += weight;
+			contrib[iDest].weight[i] = weight;
+			t += filterScale;
+        }
+
+        //**--------------------------------------------------------
+        //** Normalize weights by dividing by the sum of the weights
+        //**--------------------------------------------------------
+        if ( totalWeight > 0.0 )
+        {   
+            for ( int i = 0; i < numWeights; i++)
+            {
+                contrib[iDest].weight[i] /= totalWeight;
+            }
+        }
+    }
+}
+
+//**-------------------------------------------------------------------------
+//** Name: Filter_TwoPass( RGBCOLOR *pSrcImage, 
+//**                       int srcWidth, int srcHeight, 
+//**                       RGBCOLOR *pDestImage, 
+//**                       int destWidth, int destHeight, 
+//**                       double (*FilterProc)(double) )
+//** Returns: 0 on failure and 1 on success
+//** Description: Filters a 2d image with a two pass filter by averaging the
+//**    weighted contributions of the pixels within the filter region.  The
+//**    contributions are determined by a weighting function parameter.
+//**-------------------------------------------------------------------------
+int FilterTwoPass( RGBCOLOR *pSrcImage, int srcWidth, int srcHeight, 
+                    RGBCOLOR *pDestImage, int destWidth, int destHeight, unsigned int wrapFlags, double (*FilterProc)(double) )
+{
+    FilterWeights *contrib;
+    RGBCOLOR *pPixel;
+    RGBCOLOR *pSrcPixel;
+    RGBCOLOR *pTempImage;
+    int iRow;
+    int iCol;
+    int iSrcCol;
+    int iSrcRow;
+    int iWeight;
+    double dRed;
+    double dGreen;
+    double dBlue;
+    double dAlpha;
+    double filterSize = 3.0;
+
+	int maxDim = (srcWidth>srcHeight)?srcWidth:srcHeight;
+	contrib = (FilterWeights*)malloc(maxDim * sizeof(FilterWeights));
+
+	//**------------------------------------------------------------------------
+    //** Need to create a temporary image to stuff the horizontally scaled image
+    //**------------------------------------------------------------------------
+    pTempImage = (RGBCOLOR *)malloc( destWidth * srcHeight * sizeof(RGBCOLOR) );
+    if ( pTempImage == NULL )
+    {
+        return 0;
+    }
+
+    //**-------------------------------------------------------
+    //** Horizontally filter the image into the temporary image
+    //**-------------------------------------------------------
+	bool bWrapHorizontal = !!(wrapFlags&FILTER_WRAP_X);
+	CalcContributions( srcWidth, destWidth, filterSize, bWrapHorizontal, FilterProc, contrib );
+    for ( iRow = 0; iRow < srcHeight; iRow++ )
+    {
+        for ( iCol = 0; iCol < destWidth; iCol++ )
+        {
+            dRed   = 0;
+            dGreen = 0;
+            dBlue  = 0;
+            dAlpha = 0;
+
+            for ( iWeight = 0; iWeight < contrib[iCol].numWeights; iWeight++ )
+            {
+                iSrcCol = iWeight + contrib[iCol].first;
+				if (bWrapHorizontal)
+				{
+					iSrcCol = (iSrcCol < 0) ? (srcWidth + iSrcCol) : (iSrcCol >= srcWidth) ? (iSrcCol - srcWidth) : iSrcCol;
+				}
+                pSrcPixel = pSrcImage + (iRow * srcWidth) + iSrcCol;
+                dRed   += contrib[iCol].weight[iWeight] * pSrcPixel->rgba[0];
+                dGreen += contrib[iCol].weight[iWeight] * pSrcPixel->rgba[1];
+                dBlue  += contrib[iCol].weight[iWeight] * pSrcPixel->rgba[2];
+                dAlpha += contrib[iCol].weight[iWeight] * pSrcPixel->rgba[3];
+            }
+
+            pPixel = pTempImage + (iRow * destWidth) + iCol;
+			pPixel->rgba[0] = static_cast<unsigned char>(std::max(0.0, std::min(255.0, dRed)));
+			pPixel->rgba[1] = static_cast<unsigned char>(std::max(0.0, std::min(255.0, dGreen)));
+			pPixel->rgba[2] = static_cast<unsigned char>(std::max(0.0, std::min(255.0, dBlue)));
+			pPixel->rgba[3] = static_cast<unsigned char>(std::max(0.0, std::min(255.0, dAlpha)));
+        }
+    }
+
+    //**-------------------------------------------------------
+    //** Vertically filter the image into the destination image
+    //**-------------------------------------------------------
+	bool bWrapVertical = !!(wrapFlags&FILTER_WRAP_Y);
+	CalcContributions(srcHeight, destHeight, filterSize, bWrapVertical, FilterProc, contrib);
+    for ( iCol = 0; iCol < destWidth; iCol++ )
+    {
+        for ( iRow = 0; iRow < destHeight; iRow++ )
+        {
+            dRed   = 0;
+            dGreen = 0;
+            dBlue  = 0;
+            dAlpha = 0;
+
+            for ( iWeight = 0; iWeight < contrib[iRow].numWeights; iWeight++ )
+            {
+                iSrcRow = iWeight + contrib[iRow].first;
+				if (bWrapVertical)
+				{
+					iSrcRow = (iSrcRow < 0) ? (srcHeight + iSrcRow) : (iSrcRow >= srcHeight) ? (iSrcRow - srcHeight) : iSrcRow;
+				}
+                pSrcPixel = pTempImage + (iSrcRow * destWidth) + iCol;
+                dRed   += contrib[iRow].weight[iWeight] * pSrcPixel->rgba[0];
+                dGreen += contrib[iRow].weight[iWeight] * pSrcPixel->rgba[1];
+                dBlue  += contrib[iRow].weight[iWeight] * pSrcPixel->rgba[2];
+                dAlpha += contrib[iRow].weight[iWeight] * pSrcPixel->rgba[3];
+            }
+
+            pPixel = pDestImage + (iRow * destWidth) + iCol;
+            pPixel->rgba[0]   = (unsigned char)(std::max( 0.0, std::min( 255.0, dRed)));
+            pPixel->rgba[1] = (unsigned char)(std::max( 0.0, std::min( 255.0, dGreen)));
+            pPixel->rgba[2]  = (unsigned char)(std::max( 0.0, std::min( 255.0, dBlue)));
+            pPixel->rgba[3] = (unsigned char)(std::max( 0.0, std::min( 255.0, dAlpha)));
+        }
+    }
+
+    free( pTempImage );
+	free( contrib );
+
+    return 1;
+}
+
+//**-------------------------------------------------------------------------
+//** Name: FilterResample(RGBCOLOR *pSrcImage, int srcWidth, int srcHeight, 
+//**                       RGBCOLOR *pDstImage, int dstWidth, int dstHeight)
+//** Returns: 1
+//** Description: This function runs a 2d box filter over the srouce image
+//** to produce the destination image.
+//**-------------------------------------------------------------------------
+void FilterResample( RGBCOLOR *pSrcImage, int srcWidth, int srcHeight, 
+                     RGBCOLOR *pDstImage, int dstWidth, int dstHeight )
+{
+    int iRow;
+    int iCol;
+    int iSampleRow;
+    int iSampleCol;
+    int iFirstSampleRow;
+    int iFirstSampleCol;
+    int iLastSampleRow;
+    int iLastSampleCol;
+    int red;
+    int green;
+    int blue;
+    int alpha;
+    int samples;
+    float xScale;
+    float yScale;
+
+    RGBCOLOR *pSrcPixel;
+    RGBCOLOR *pDstPixel;
+
+    xScale = (float)srcWidth / dstWidth;
+    yScale = (float)srcHeight / dstHeight;
+
+    for ( iRow = 0; iRow < dstHeight; iRow++ )
+    {
+        for ( iCol = 0; iCol < dstWidth; iCol++ )
+        {
+            iFirstSampleRow = (int)(iRow * yScale);
+            iLastSampleRow = (int)ceil(iFirstSampleRow + yScale - 1);
+            if ( iLastSampleRow >= srcHeight )
+            {
+                iLastSampleRow = srcHeight - 1;
+            }
+
+            iFirstSampleCol = (int)(iCol * xScale);
+            iLastSampleCol = (int)ceil(iFirstSampleCol + xScale - 1);
+            if ( iLastSampleCol >= srcWidth )
+            {
+                iLastSampleCol = srcWidth - 1;
+            }
+
+            samples = 0;
+            red     = 0;
+            green   = 0;
+            blue    = 0;
+            alpha   = 0;
+            for ( iSampleRow = iFirstSampleRow; iSampleRow <= iLastSampleRow; iSampleRow++ )
+            {
+                for ( iSampleCol = iFirstSampleCol; iSampleCol <= iLastSampleCol; iSampleCol++ )
+                {
+                    pSrcPixel = pSrcImage + iSampleRow * srcWidth + iSampleCol;
+                    red   += pSrcPixel->rgba[0];
+                    green += pSrcPixel->rgba[1];
+                    blue  += pSrcPixel->rgba[2];
+                    alpha += pSrcPixel->rgba[3];
+
+                    samples++;
+                }
+            }
+
+            pDstPixel = pDstImage + iRow * dstWidth + iCol;
+            if ( samples > 0 )
+            {
+                pDstPixel->rgba[0] = static_cast<uint8_t>(red / samples);
+                pDstPixel->rgba[1] = static_cast<uint8_t>(green / samples);
+                pDstPixel->rgba[2] = static_cast<uint8_t>(blue / samples);
+                pDstPixel->rgba[3] = static_cast<uint8_t>(alpha / samples);
+            }
+            else
+            {
+                pDstPixel->rgba[0] = static_cast<uint8_t>(red);
+                pDstPixel->rgba[1] = static_cast<uint8_t>(green);
+                pDstPixel->rgba[2] = static_cast<uint8_t>(blue);
+                pDstPixel->rgba[3] = static_cast<uint8_t>(alpha);
+            }
+        }
+    }
+}
+
+
+}

+ 244 - 0
thirdparty/etc2comp/EtcFilter.h

@@ -0,0 +1,244 @@
+#pragma once
+#include <stdint.h>
+#include <algorithm>
+
+namespace Etc
+{
+
+enum FilterEnums
+{
+	MaxFilterSize = 32
+};
+
+enum WrapFlags
+{
+	FILTER_WRAP_NONE = 0,
+	FILTER_WRAP_X = 0x1,
+	FILTER_WRAP_Y = 0x2
+};
+
+typedef struct tagFilterWeights
+{
+	int   first;
+	int   numWeights;
+	double weight[MaxFilterSize * 2 + 1];
+} FilterWeights;
+
+typedef struct tagRGBCOLOR
+{
+	union
+	{
+		uint32_t ulColor;
+		uint8_t rgba[4];
+	};
+} RGBCOLOR;
+
+
+double FilterBox( double t );
+double FilterLinear( double t );
+double FilterLanczos3( double t );
+
+int FilterTwoPass( RGBCOLOR *pSrcImage, int srcWidth, int srcHeight, 
+                    RGBCOLOR *pDestImage, int destWidth, int destHeight, unsigned int wrapFlags, double (*FilterProc)(double) );
+void FilterResample( RGBCOLOR *pSrcImage, int srcWidth, int srcHeight, 
+                     RGBCOLOR *pDstImage, int dstWidth, int dstHeight );
+
+
+void CalcContributions(int srcSize, int destSize, double filterSize, bool wrap, double(*FilterProc)(double), FilterWeights contrib[]);
+
+template <typename T>
+void FilterResample(T *pSrcImage, int srcWidth, int srcHeight, T *pDstImage, int dstWidth, int dstHeight)
+{
+	float xScale;
+	float yScale;
+
+	T *pSrcPixel;
+	T *pDstPixel;
+
+	xScale = (float)srcWidth / dstWidth;
+	yScale = (float)srcHeight / dstHeight;
+
+	for (int iRow = 0; iRow < dstHeight; iRow++)
+	{
+		for (int iCol = 0; iCol < dstWidth; iCol++)
+		{
+			int samples;
+			int iFirstSampleRow;
+			int iFirstSampleCol;
+			int iLastSampleRow;
+			int iLastSampleCol;
+			float red;
+			float green;
+			float blue;
+			float alpha;
+
+			iFirstSampleRow = (int)(iRow * yScale);
+			iLastSampleRow = (int)ceil(iFirstSampleRow + yScale - 1);
+			if (iLastSampleRow >= srcHeight)
+			{
+				iLastSampleRow = srcHeight - 1;
+			}
+
+			iFirstSampleCol = (int)(iCol * xScale);
+			iLastSampleCol = (int)ceil(iFirstSampleCol + xScale - 1);
+			if (iLastSampleCol >= srcWidth)
+			{
+				iLastSampleCol = srcWidth - 1;
+			}
+
+			samples = 0;
+			red = 0.f;
+			green = 0.f;
+			blue = 0.f;
+			alpha = 0.f;
+			for (int iSampleRow = iFirstSampleRow; iSampleRow <= iLastSampleRow; iSampleRow++)
+			{
+				for (int iSampleCol = iFirstSampleCol; iSampleCol <= iLastSampleCol; iSampleCol++)
+				{
+					pSrcPixel = pSrcImage + (iSampleRow * srcWidth + iSampleCol) * 4;
+					red += static_cast<float>(pSrcPixel[0]);
+					green += static_cast<float>(pSrcPixel[1]);
+					blue += static_cast<float>(pSrcPixel[2]);
+					alpha += static_cast<float>(pSrcPixel[3]);
+
+					samples++;
+				}
+			}
+
+			pDstPixel = pDstImage + (iRow * dstWidth + iCol) * 4;
+			if (samples > 0)
+			{
+				pDstPixel[0] = static_cast<T>(red / samples);
+				pDstPixel[1] = static_cast<T>(green / samples);
+				pDstPixel[2] = static_cast<T>(blue / samples);
+				pDstPixel[3] = static_cast<T>(alpha / samples);
+			}
+			else
+			{
+				pDstPixel[0] = static_cast<T>(red);
+				pDstPixel[1] = static_cast<T>(green);
+				pDstPixel[2] = static_cast<T>(blue);
+				pDstPixel[3] = static_cast<T>(alpha);
+			}
+		}
+	}
+
+}
+
+//**-------------------------------------------------------------------------
+//** Name: Filter_TwoPass( RGBCOLOR *pSrcImage, 
+//**                       int srcWidth, int srcHeight, 
+//**                       RGBCOLOR *pDestImage, 
+//**                       int destWidth, int destHeight, 
+//**                       double (*FilterProc)(double) )
+//** Returns: 0 on failure and 1 on success
+//** Description: Filters a 2d image with a two pass filter by averaging the
+//**    weighted contributions of the pixels within the filter region.  The
+//**    contributions are determined by a weighting function parameter.
+//**-------------------------------------------------------------------------
+template <typename T>
+int FilterTwoPass(T *pSrcImage, int srcWidth, int srcHeight,
+	T *pDestImage, int destWidth, int destHeight, unsigned int wrapFlags, double(*FilterProc)(double))
+{
+	const int numComponents = 4;
+	FilterWeights *contrib;
+	T *pPixel;
+	T *pTempImage;
+	double dRed;
+	double dGreen;
+	double dBlue;
+	double dAlpha;
+	double filterSize = 3.0;
+
+	int maxDim = (srcWidth>srcHeight) ? srcWidth : srcHeight;
+	contrib = new FilterWeights[maxDim];
+
+	//**------------------------------------------------------------------------
+	//** Need to create a temporary image to stuff the horizontally scaled image
+	//**------------------------------------------------------------------------
+	pTempImage = new T[destWidth * srcHeight * numComponents];
+	if (pTempImage == NULL)
+	{
+		return 0;
+	}
+
+	//**-------------------------------------------------------
+	//** Horizontally filter the image into the temporary image
+	//**-------------------------------------------------------
+	bool bWrapHorizontal = !!(wrapFlags&FILTER_WRAP_X);
+	CalcContributions(srcWidth, destWidth, filterSize, bWrapHorizontal, FilterProc, contrib);
+	for (int iRow = 0; iRow < srcHeight; iRow++)
+	{
+		for (int iCol = 0; iCol < destWidth; iCol++)
+		{
+			dRed = 0;
+			dGreen = 0;
+			dBlue = 0;
+			dAlpha = 0;
+
+			for (int iWeight = 0; iWeight < contrib[iCol].numWeights; iWeight++)
+			{
+				int iSrcCol = iWeight + contrib[iCol].first;
+				if(bWrapHorizontal)
+				{
+					iSrcCol = (iSrcCol < 0)?(srcWidth+iSrcCol):(iSrcCol >= srcWidth)?(iSrcCol-srcWidth):iSrcCol;
+				}
+				T* pSrcPixel = pSrcImage + ((iRow * srcWidth) + iSrcCol)*numComponents;
+				dRed += contrib[iCol].weight[iWeight] * pSrcPixel[0];
+				dGreen += contrib[iCol].weight[iWeight] * pSrcPixel[1];
+				dBlue += contrib[iCol].weight[iWeight] * pSrcPixel[2];
+				dAlpha += contrib[iCol].weight[iWeight] * pSrcPixel[3];
+			}
+
+			pPixel = pTempImage + ((iRow * destWidth) + iCol)*numComponents;
+			pPixel[0] = static_cast<T>(std::max(0.0, std::min(255.0, dRed)));
+			pPixel[1] = static_cast<T>(std::max(0.0, std::min(255.0, dGreen)));
+			pPixel[2] = static_cast<T>(std::max(0.0, std::min(255.0, dBlue)));
+			pPixel[3] = static_cast<T>(std::max(0.0, std::min(255.0, dAlpha)));
+		}
+	}
+
+	//**-------------------------------------------------------
+	//** Vertically filter the image into the destination image
+	//**-------------------------------------------------------
+	bool bWrapVertical = !!(wrapFlags&FILTER_WRAP_Y);
+	CalcContributions(srcHeight, destHeight, filterSize, bWrapVertical, FilterProc, contrib);
+	for (int iCol = 0; iCol < destWidth; iCol++)
+	{
+		for (int iRow = 0; iRow < destHeight; iRow++)
+		{
+			dRed = 0;
+			dGreen = 0;
+			dBlue = 0;
+			dAlpha = 0;
+
+			for (int iWeight = 0; iWeight < contrib[iRow].numWeights; iWeight++)
+			{
+				int iSrcRow = iWeight + contrib[iRow].first;
+				if (bWrapVertical)
+				{
+					iSrcRow = (iSrcRow < 0) ? (srcHeight + iSrcRow) : (iSrcRow >= srcHeight) ? (iSrcRow - srcHeight) : iSrcRow;
+				}
+				T* pSrcPixel = pTempImage + ((iSrcRow * destWidth) + iCol)*numComponents;
+				dRed += contrib[iRow].weight[iWeight] * pSrcPixel[0];
+				dGreen += contrib[iRow].weight[iWeight] * pSrcPixel[1];
+				dBlue += contrib[iRow].weight[iWeight] * pSrcPixel[2];
+				dAlpha += contrib[iRow].weight[iWeight] * pSrcPixel[3];
+			}
+
+			pPixel = pDestImage + ((iRow * destWidth) + iCol)*numComponents;
+			pPixel[0] = static_cast<T>(std::max(0.0, std::min(255.0, dRed)));
+			pPixel[1] = static_cast<T>(std::max(0.0, std::min(255.0, dGreen)));
+			pPixel[2] = static_cast<T>(std::max(0.0, std::min(255.0, dBlue)));
+			pPixel[3] = static_cast<T>(std::max(0.0, std::min(255.0, dAlpha)));
+		}
+	}
+
+	delete[] pTempImage;
+	delete[] contrib;
+
+	return 1;
+}
+
+
+}

+ 685 - 0
thirdparty/etc2comp/EtcImage.cpp

@@ -0,0 +1,685 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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.
+ */
+
+/*
+EtcImage.cpp
+
+Image is an array of 4x4 blocks that represent the encoding of the source image
+
+*/
+
+#include "EtcConfig.h"
+
+#include <stdlib.h>
+
+#include "EtcImage.h"
+
+#include "Etc.h"
+#include "EtcBlock4x4.h"
+#include "EtcBlock4x4EncodingBits.h"
+#include "EtcSortedBlockList.h"
+
+#if ETC_WINDOWS
+#include <windows.h>
+#endif
+#include <ctime>
+#include <chrono>
+#include <future>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+// fix conflict with Block4x4::AlphaMix
+#ifdef OPAQUE
+#undef OPAQUE
+#endif
+#ifdef TRANSPARENT
+#undef TRANSPARENT
+#endif
+
+namespace Etc
+{
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+	Image::Image(void)
+	{
+		m_encodingStatus = EncodingStatus::SUCCESS;
+		m_warningsToCapture = EncodingStatus::SUCCESS;
+		m_pafrgbaSource = nullptr;
+
+		m_pablock = nullptr;
+
+		m_encodingbitsformat = Block4x4EncodingBits::Format::UNKNOWN;
+		m_uiEncodingBitsBytes = 0;
+		m_paucEncodingBits = nullptr;
+
+		m_format = Format::UNKNOWN;
+		m_iNumOpaquePixels = 0;
+		m_iNumTranslucentPixels = 0;
+		m_iNumTransparentPixels = 0;
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// constructor using source image
+	// used to set state before Encode() is called
+	//
+	Image::Image(float *a_pafSourceRGBA, unsigned int a_uiSourceWidth,
+					unsigned int a_uiSourceHeight, 
+					ErrorMetric a_errormetric)
+	{
+		m_encodingStatus = EncodingStatus::SUCCESS;
+		m_warningsToCapture = EncodingStatus::SUCCESS;
+		m_pafrgbaSource = (ColorFloatRGBA *) a_pafSourceRGBA;
+		m_uiSourceWidth = a_uiSourceWidth;
+		m_uiSourceHeight = a_uiSourceHeight;
+
+		m_uiExtendedWidth = CalcExtendedDimension((unsigned short)m_uiSourceWidth);
+		m_uiExtendedHeight = CalcExtendedDimension((unsigned short)m_uiSourceHeight);
+
+		m_uiBlockColumns = m_uiExtendedWidth >> 2;
+		m_uiBlockRows = m_uiExtendedHeight >> 2;
+
+		m_pablock = new Block4x4[GetNumberOfBlocks()];
+		assert(m_pablock);
+
+		m_format = Format::UNKNOWN;
+
+		m_encodingbitsformat = Block4x4EncodingBits::Format::UNKNOWN;
+		m_uiEncodingBitsBytes = 0;
+		m_paucEncodingBits = nullptr;
+
+		m_errormetric = a_errormetric;
+		m_fEffort = 0.0f;
+
+		m_iEncodeTime_ms = -1;
+
+		m_iNumOpaquePixels = 0;
+		m_iNumTranslucentPixels = 0;
+		m_iNumTransparentPixels = 0;
+		m_bVerboseOutput = false;
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// constructor using encoding bits
+	// recreates encoding state using a previously encoded image
+	//
+	Image::Image(Format a_format,
+					unsigned int a_uiSourceWidth, unsigned int a_uiSourceHeight,
+					unsigned char *a_paucEncidingBits, unsigned int a_uiEncodingBitsBytes,
+					Image *a_pimageSource, ErrorMetric a_errormetric)
+	{
+		m_encodingStatus = EncodingStatus::SUCCESS;
+		m_pafrgbaSource = nullptr;
+		m_uiSourceWidth = a_uiSourceWidth;
+		m_uiSourceHeight = a_uiSourceHeight;
+
+		m_uiExtendedWidth = CalcExtendedDimension((unsigned short)m_uiSourceWidth);
+		m_uiExtendedHeight = CalcExtendedDimension((unsigned short)m_uiSourceHeight);
+
+		m_uiBlockColumns = m_uiExtendedWidth >> 2;
+		m_uiBlockRows = m_uiExtendedHeight >> 2;
+
+		unsigned int uiBlocks = GetNumberOfBlocks();
+
+		m_pablock = new Block4x4[uiBlocks];
+		assert(m_pablock);
+
+		m_format = a_format;
+
+		m_iNumOpaquePixels = 0;
+		m_iNumTranslucentPixels = 0;
+		m_iNumTransparentPixels = 0;
+		
+		m_encodingbitsformat = DetermineEncodingBitsFormat(m_format);
+		if (m_encodingbitsformat == Block4x4EncodingBits::Format::UNKNOWN)
+		{
+			AddToEncodingStatus(ERROR_UNKNOWN_FORMAT);
+			return;
+		}
+		m_uiEncodingBitsBytes = a_uiEncodingBitsBytes;
+		m_paucEncodingBits = a_paucEncidingBits;
+
+		m_errormetric = a_errormetric;
+		m_fEffort = 0.0f;
+		m_bVerboseOutput = false;
+		m_iEncodeTime_ms = -1;
+		
+		unsigned char *paucEncodingBits = m_paucEncodingBits;
+		unsigned int uiEncodingBitsBytesPerBlock = Block4x4EncodingBits::GetBytesPerBlock(m_encodingbitsformat);
+
+		unsigned int uiH = 0;
+		unsigned int uiV = 0;
+		for (unsigned int uiBlock = 0; uiBlock < uiBlocks; uiBlock++)
+		{
+			m_pablock[uiBlock].InitFromEtcEncodingBits(a_format, uiH, uiV, paucEncodingBits, 
+														a_pimageSource, a_errormetric);
+			paucEncodingBits += uiEncodingBitsBytesPerBlock;
+			uiH += 4;
+			if (uiH >= m_uiSourceWidth)
+			{
+				uiH = 0;
+				uiV += 4;
+			}
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+	Image::~Image(void)
+	{
+		if (m_pablock != nullptr)
+		{
+			delete[] m_pablock;
+			m_pablock = nullptr;
+		}
+
+		/*if (m_paucEncodingBits != nullptr)
+		{
+			delete[] m_paucEncodingBits;
+			m_paucEncodingBits = nullptr;
+		}*/
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// encode an image
+	// create a set of encoding bits that conforms to a_format
+	// find best fit using a_errormetric
+	// explore a range of possible encodings based on a_fEffort (range = [0:100])
+	// speed up process using a_uiJobs as the number of process threads (a_uiJobs must not excede a_uiMaxJobs)
+	//
+	Image::EncodingStatus Image::Encode(Format a_format, ErrorMetric a_errormetric, float a_fEffort, unsigned int a_uiJobs, unsigned int a_uiMaxJobs)
+	{
+
+		auto start = std::chrono::steady_clock::now();
+		
+		m_encodingStatus = EncodingStatus::SUCCESS;
+
+		m_format = a_format;
+		m_errormetric = a_errormetric;
+		m_fEffort = a_fEffort;
+
+		if (m_errormetric < 0 || m_errormetric > ERROR_METRICS)
+		{
+			AddToEncodingStatus(ERROR_UNKNOWN_ERROR_METRIC);
+			return m_encodingStatus;
+		}
+
+		if (m_fEffort < ETCCOMP_MIN_EFFORT_LEVEL)
+		{
+			AddToEncodingStatus(WARNING_EFFORT_OUT_OF_RANGE);
+			m_fEffort = ETCCOMP_MIN_EFFORT_LEVEL;
+		}
+		else if (m_fEffort > ETCCOMP_MAX_EFFORT_LEVEL)
+		{
+			AddToEncodingStatus(WARNING_EFFORT_OUT_OF_RANGE);
+			m_fEffort = ETCCOMP_MAX_EFFORT_LEVEL;
+		}
+		if (a_uiJobs < 1)
+		{
+			a_uiJobs = 1;
+			AddToEncodingStatus(WARNING_JOBS_OUT_OF_RANGE);
+		}
+		else if (a_uiJobs > a_uiMaxJobs)
+		{
+			a_uiJobs = a_uiMaxJobs;
+			AddToEncodingStatus(WARNING_JOBS_OUT_OF_RANGE);
+		}
+
+		m_encodingbitsformat = DetermineEncodingBitsFormat(m_format);
+
+		if (m_encodingbitsformat == Block4x4EncodingBits::Format::UNKNOWN)
+		{
+			AddToEncodingStatus(ERROR_UNKNOWN_FORMAT);
+			return m_encodingStatus;
+		}
+
+		assert(m_paucEncodingBits == nullptr);
+		m_uiEncodingBitsBytes = GetNumberOfBlocks() * Block4x4EncodingBits::GetBytesPerBlock(m_encodingbitsformat);
+		m_paucEncodingBits = new unsigned char[m_uiEncodingBitsBytes];
+
+		InitBlocksAndBlockSorter();
+
+
+		std::future<void> *handle = new std::future<void>[a_uiMaxJobs];
+
+		unsigned int uiNumThreadsNeeded = 0;
+		unsigned int uiUnfinishedBlocks = GetNumberOfBlocks();
+
+		uiNumThreadsNeeded = (uiUnfinishedBlocks < a_uiJobs) ? uiUnfinishedBlocks : a_uiJobs;
+			
+		for (int i = 0; i < (int)uiNumThreadsNeeded - 1; i++)
+		{
+			handle[i] = async(std::launch::async, &Image::RunFirstPass, this, i, uiNumThreadsNeeded);
+		}
+
+		RunFirstPass(uiNumThreadsNeeded - 1, uiNumThreadsNeeded);
+
+		for (int i = 0; i < (int)uiNumThreadsNeeded - 1; i++)
+		{
+			handle[i].get();
+		}
+
+		// perform effort-based encoding
+		if (m_fEffort > ETCCOMP_MIN_EFFORT_LEVEL)
+		{
+			unsigned int uiFinishedBlocks = 0;
+			unsigned int uiTotalEffortBlocks = static_cast<unsigned int>(roundf(0.01f * m_fEffort  * GetNumberOfBlocks()));
+
+			if (m_bVerboseOutput)
+			{
+				printf("effortblocks = %d\n", uiTotalEffortBlocks);
+			}
+			unsigned int uiPass = 0;
+			while (1)
+			{
+				if (m_bVerboseOutput)
+				{
+					uiPass++;
+					printf("pass %u\n", uiPass);
+				}
+				m_psortedblocklist->Sort();
+				uiUnfinishedBlocks = m_psortedblocklist->GetNumberOfSortedBlocks();
+				uiFinishedBlocks = GetNumberOfBlocks() - uiUnfinishedBlocks;
+				if (m_bVerboseOutput)
+				{
+					printf("    %u unfinished blocks\n", uiUnfinishedBlocks);
+					// m_psortedblocklist->Print();
+				}
+
+				
+
+				//stop enocding when we did enough to satify the effort percentage
+				if (uiFinishedBlocks >= uiTotalEffortBlocks)
+				{
+					if (m_bVerboseOutput)
+					{
+						printf("Finished %d Blocks out of %d\n", uiFinishedBlocks, uiTotalEffortBlocks);
+					}
+					break;
+				}
+
+				unsigned int uiIteratedBlocks = 0;
+				unsigned int blocksToIterateThisPass = (uiTotalEffortBlocks - uiFinishedBlocks);
+				uiNumThreadsNeeded = (uiUnfinishedBlocks < a_uiJobs) ? uiUnfinishedBlocks : a_uiJobs;
+
+				if (uiNumThreadsNeeded <= 1)
+				{
+					//since we already how many blocks each thread will process
+					//cap the thread limit to do the proper amount of work, and not more
+					uiIteratedBlocks = IterateThroughWorstBlocks(blocksToIterateThisPass, 0, 1);
+				}
+				else
+				{
+					//we have a lot of work to do, so lets multi thread it
+					std::future<unsigned int> *handleToBlockEncoders = new std::future<unsigned int>[uiNumThreadsNeeded-1];
+
+					for (int i = 0; i < (int)uiNumThreadsNeeded - 1; i++)
+					{
+						handleToBlockEncoders[i] = async(std::launch::async, &Image::IterateThroughWorstBlocks, this, blocksToIterateThisPass, i, uiNumThreadsNeeded);
+					}
+					uiIteratedBlocks = IterateThroughWorstBlocks(blocksToIterateThisPass, uiNumThreadsNeeded - 1, uiNumThreadsNeeded);
+
+					for (int i = 0; i < (int)uiNumThreadsNeeded - 1; i++)
+					{
+						uiIteratedBlocks += handleToBlockEncoders[i].get();
+					}
+
+					delete[] handleToBlockEncoders;
+				}
+
+				if (m_bVerboseOutput)
+				{
+					printf("    %u iterated blocks\n", uiIteratedBlocks);
+				}
+			}
+		}
+
+		// generate Etc2-compatible bit-format 4x4 blocks
+		for (int i = 0; i < (int)a_uiJobs - 1; i++)
+		{
+			handle[i] = async(std::launch::async, &Image::SetEncodingBits, this, i, a_uiJobs);
+		}
+		SetEncodingBits(a_uiJobs - 1, a_uiJobs);
+
+		for (int i = 0; i < (int)a_uiJobs - 1; i++)
+		{
+			handle[i].get();
+		}
+
+		auto end = std::chrono::steady_clock::now();
+		std::chrono::milliseconds elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
+		m_iEncodeTime_ms = (int)elapsed.count();
+
+		delete[] handle;
+		delete m_psortedblocklist;
+		return m_encodingStatus;
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// iterate the encoding thru the blocks with the worst error
+	// stop when a_uiMaxBlocks blocks have been iterated
+	// split the blocks between the process threads using a_uiMultithreadingOffset and a_uiMultithreadingStride
+	//
+	unsigned int Image::IterateThroughWorstBlocks(unsigned int a_uiMaxBlocks, 
+													unsigned int a_uiMultithreadingOffset, 
+													unsigned int a_uiMultithreadingStride)
+	{
+		assert(a_uiMultithreadingStride > 0);
+		unsigned int uiIteratedBlocks = a_uiMultithreadingOffset;
+
+		SortedBlockList::Link *plink = m_psortedblocklist->GetLinkToFirstBlock();
+		for (plink = plink->Advance(a_uiMultithreadingOffset);
+				plink != nullptr;
+				plink = plink->Advance(a_uiMultithreadingStride) )
+		{
+			if (uiIteratedBlocks >= a_uiMaxBlocks)
+			{
+				break;
+			}
+
+			plink->GetBlock()->PerformEncodingIteration(m_fEffort);
+
+			uiIteratedBlocks += a_uiMultithreadingStride;	
+		}
+
+		return uiIteratedBlocks;
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// determine which warnings to check for during Encode() based on encoding format
+	//
+	void Image::FindEncodingWarningTypesForCurFormat()
+	{
+		TrackEncodingWarning(WARNING_ALL_TRANSPARENT_PIXELS);
+		TrackEncodingWarning(WARNING_SOME_RGBA_NOT_0_TO_1);
+		switch (m_format)
+		{
+		case Image::Format::ETC1:
+		case Image::Format::RGB8:
+		case Image::Format::SRGB8:
+			TrackEncodingWarning(WARNING_SOME_NON_OPAQUE_PIXELS);
+			TrackEncodingWarning(WARNING_SOME_TRANSLUCENT_PIXELS);
+			break;
+
+		case Image::Format::RGB8A1:
+		case Image::Format::SRGB8A1:
+			TrackEncodingWarning(WARNING_SOME_TRANSLUCENT_PIXELS);
+			TrackEncodingWarning(WARNING_ALL_OPAQUE_PIXELS);
+			break;
+		case Image::Format::RGBA8:
+		case Image::Format::SRGBA8:
+			TrackEncodingWarning(WARNING_ALL_OPAQUE_PIXELS);
+			break;
+
+		case Image::Format::R11:
+		case Image::Format::SIGNED_R11:
+			TrackEncodingWarning(WARNING_SOME_NON_OPAQUE_PIXELS);
+			TrackEncodingWarning(WARNING_SOME_TRANSLUCENT_PIXELS);
+			TrackEncodingWarning(WARNING_SOME_GREEN_VALUES_ARE_NOT_ZERO);
+			TrackEncodingWarning(WARNING_SOME_BLUE_VALUES_ARE_NOT_ZERO);
+			break;
+
+		case Image::Format::RG11:
+		case Image::Format::SIGNED_RG11:
+			TrackEncodingWarning(WARNING_SOME_NON_OPAQUE_PIXELS);
+			TrackEncodingWarning(WARNING_SOME_TRANSLUCENT_PIXELS);
+			TrackEncodingWarning(WARNING_SOME_BLUE_VALUES_ARE_NOT_ZERO);
+			break;
+		case Image::Format::FORMATS:
+		case Image::Format::UNKNOWN:
+		default:
+			assert(0);
+			break;
+		}
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// examine source pixels to check for warnings
+	//
+	void Image::FindAndSetEncodingWarnings()
+	{
+		int numPixels = (m_uiBlockRows * 4) * (m_uiBlockColumns * 4);
+		if (m_iNumOpaquePixels == numPixels)
+		{
+			AddToEncodingStatusIfSignfigant(Image::EncodingStatus::WARNING_ALL_OPAQUE_PIXELS);
+		}
+		if (m_iNumOpaquePixels < numPixels)
+		{
+			AddToEncodingStatusIfSignfigant(Image::EncodingStatus::WARNING_SOME_NON_OPAQUE_PIXELS);
+		}
+		if (m_iNumTranslucentPixels > 0)
+		{
+			AddToEncodingStatusIfSignfigant(Image::EncodingStatus::WARNING_SOME_TRANSLUCENT_PIXELS);
+		}
+		if (m_iNumTransparentPixels == numPixels)
+		{
+			AddToEncodingStatusIfSignfigant(Image::EncodingStatus::WARNING_ALL_TRANSPARENT_PIXELS);
+		}
+		if (m_numColorValues.fB > 0.0f)
+		{
+			AddToEncodingStatusIfSignfigant(Image::EncodingStatus::WARNING_SOME_BLUE_VALUES_ARE_NOT_ZERO);
+		}
+		if (m_numColorValues.fG > 0.0f) 
+		{
+			AddToEncodingStatusIfSignfigant(Image::EncodingStatus::WARNING_SOME_GREEN_VALUES_ARE_NOT_ZERO);
+		}
+
+		if (m_numOutOfRangeValues.fR > 0.0f || m_numOutOfRangeValues.fG > 0.0f)
+		{
+			AddToEncodingStatusIfSignfigant(Image::EncodingStatus::WARNING_SOME_RGBA_NOT_0_TO_1);
+		}
+		if (m_numOutOfRangeValues.fB > 0.0f || m_numOutOfRangeValues.fA > 0.0f)
+		{
+			AddToEncodingStatusIfSignfigant(Image::EncodingStatus::WARNING_SOME_RGBA_NOT_0_TO_1);
+		}
+	}
+	
+	// ----------------------------------------------------------------------------------------------------
+	// return a string name for a given image format
+	//
+	const char * Image::EncodingFormatToString(Image::Format a_format)
+	{
+		switch (a_format)
+		{
+		case Image::Format::ETC1:
+			return "ETC1";
+		case Image::Format::RGB8:
+			return "RGB8";
+		case Image::Format::SRGB8:
+			return "SRGB8";
+
+		case Image::Format::RGB8A1:
+			return "RGB8A1";
+		case Image::Format::SRGB8A1:
+			return "SRGB8A1";
+		case Image::Format::RGBA8:
+			return "RGBA8";
+		case Image::Format::SRGBA8:
+			return "SRGBA8";
+
+		case Image::Format::R11:
+			return "R11";
+		case Image::Format::SIGNED_R11:
+			return "SIGNED_R11";
+
+		case Image::Format::RG11:
+			return "RG11";
+		case Image::Format::SIGNED_RG11:
+			return "SIGNED_RG11";
+		case Image::Format::FORMATS:
+		case Image::Format::UNKNOWN:
+		default:
+			return "UNKNOWN";
+		}
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// return a string name for the image's format
+	//
+	const char * Image::EncodingFormatToString(void)
+	{
+		return EncodingFormatToString(m_format);
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// init image blocks prior to encoding
+	// init block sorter for subsequent sortings
+	// check for encoding warnings
+	//
+	void Image::InitBlocksAndBlockSorter(void)
+	{
+		
+		FindEncodingWarningTypesForCurFormat();
+
+		// init each block
+		Block4x4 *pblock = m_pablock;
+		unsigned char *paucEncodingBits = m_paucEncodingBits;
+		for (unsigned int uiBlockRow = 0; uiBlockRow < m_uiBlockRows; uiBlockRow++)
+		{
+			unsigned int uiBlockV = uiBlockRow * 4;
+
+			for (unsigned int uiBlockColumn = 0; uiBlockColumn < m_uiBlockColumns; uiBlockColumn++)
+			{
+				unsigned int uiBlockH = uiBlockColumn * 4;
+
+				pblock->InitFromSource(this, uiBlockH, uiBlockV, paucEncodingBits, m_errormetric);
+
+				paucEncodingBits += Block4x4EncodingBits::GetBytesPerBlock(m_encodingbitsformat);
+
+				pblock++;
+			}
+		}
+
+		FindAndSetEncodingWarnings();
+
+		// init block sorter
+		{
+			m_psortedblocklist = new SortedBlockList(GetNumberOfBlocks(), 100);
+
+			for (unsigned int uiBlock = 0; uiBlock < GetNumberOfBlocks(); uiBlock++)
+			{
+				pblock = &m_pablock[uiBlock];
+				m_psortedblocklist->AddBlock(pblock);
+			}
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// run the first pass of the encoder
+	// the encoder generally finds a reasonable, fast encoding
+	// this is run on all blocks regardless of effort to ensure that all blocks have a valid encoding
+	//
+	void Image::RunFirstPass(unsigned int a_uiMultithreadingOffset, unsigned int a_uiMultithreadingStride)
+	{
+		assert(a_uiMultithreadingStride > 0);
+
+		for (unsigned int uiBlock = a_uiMultithreadingOffset;
+				uiBlock < GetNumberOfBlocks(); 
+				uiBlock += a_uiMultithreadingStride)
+		{
+			Block4x4 *pblock = &m_pablock[uiBlock];
+			pblock->PerformEncodingIteration(m_fEffort);
+		}
+	}
+
+    // ----------------------------------------------------------------------------------------------------
+	// set the encoding bits (for the output file) based on the best encoding for each block
+	//
+	void Image::SetEncodingBits(unsigned int a_uiMultithreadingOffset,
+								unsigned int a_uiMultithreadingStride)
+	{
+		assert(a_uiMultithreadingStride > 0);
+
+		for (unsigned int uiBlock = a_uiMultithreadingOffset; 
+				uiBlock < GetNumberOfBlocks(); 
+				uiBlock += a_uiMultithreadingStride)
+		{
+			Block4x4 *pblock = &m_pablock[uiBlock];
+			pblock->SetEncodingBitsFromEncoding();
+		}
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// return the image error
+	// image error is the sum of all block errors
+	//
+	float Image::GetError(void)
+	{
+		float fError = 0.0f;
+
+		for (unsigned int uiBlock = 0; uiBlock < GetNumberOfBlocks(); uiBlock++)
+		{
+			Block4x4 *pblock = &m_pablock[uiBlock];
+			fError += pblock->GetError();
+		}
+
+		return fError;
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// determine the encoding bits format based on the encoding format
+	// the encoding bits format is a family of bit encodings that are shared across various encoding formats
+	//
+	Block4x4EncodingBits::Format Image::DetermineEncodingBitsFormat(Format a_format)
+	{
+		Block4x4EncodingBits::Format encodingbitsformat;
+
+		// determine encoding bits format from image format
+		switch (a_format)
+		{
+		case Format::ETC1:
+		case Format::RGB8:
+		case Format::SRGB8:
+			encodingbitsformat = Block4x4EncodingBits::Format::RGB8;
+			break;
+
+		case Format::RGBA8:
+		case Format::SRGBA8:
+			encodingbitsformat = Block4x4EncodingBits::Format::RGBA8;
+			break;
+
+		case Format::R11:
+		case Format::SIGNED_R11:
+			encodingbitsformat = Block4x4EncodingBits::Format::R11;
+			break;
+
+		case Format::RG11:
+		case Format::SIGNED_RG11:
+			encodingbitsformat = Block4x4EncodingBits::Format::RG11;
+			break;
+
+		case Format::RGB8A1:
+		case Format::SRGB8A1:
+			encodingbitsformat = Block4x4EncodingBits::Format::RGB8A1;
+			break;
+
+		default:
+			encodingbitsformat = Block4x4EncodingBits::Format::UNKNOWN;
+			break;
+		}
+
+		return encodingbitsformat;
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+
+}	// namespace Etc

+ 249 - 0
thirdparty/etc2comp/EtcImage.h

@@ -0,0 +1,249 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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.
+ */
+
+#pragma once
+
+//#include "Etc.h"
+#include "EtcColorFloatRGBA.h"
+#include "EtcBlock4x4EncodingBits.h"
+#include "EtcErrorMetric.h"
+
+
+namespace Etc
+{
+	class Block4x4;
+	class EncoderSpec;
+	class SortedBlockList;
+
+    class Image
+    {
+    public:
+
+		//the differnt warning and errors that can come up during encoding
+		enum  EncodingStatus
+		{
+			SUCCESS = 0,
+			//
+			WARNING_THRESHOLD = 1 << 0,
+			//
+			WARNING_EFFORT_OUT_OF_RANGE = 1 << 1,
+			WARNING_JOBS_OUT_OF_RANGE = 1 << 2,
+			WARNING_SOME_NON_OPAQUE_PIXELS = 1 << 3,//just for opaque formats, etc1, rgb8, r11, rg11
+			WARNING_ALL_OPAQUE_PIXELS = 1 << 4,
+			WARNING_ALL_TRANSPARENT_PIXELS = 1 << 5,
+			WARNING_SOME_TRANSLUCENT_PIXELS = 1 << 6,//just for rgb8A1
+			WARNING_SOME_RGBA_NOT_0_TO_1 = 1 << 7,
+			WARNING_SOME_BLUE_VALUES_ARE_NOT_ZERO = 1 << 8,
+			WARNING_SOME_GREEN_VALUES_ARE_NOT_ZERO = 1 << 9,
+			//
+			ERROR_THRESHOLD = 1 << 16,
+			//
+			ERROR_UNKNOWN_FORMAT = 1 << 17,
+			ERROR_UNKNOWN_ERROR_METRIC = 1 << 18,
+			ERROR_ZERO_WIDTH_OR_HEIGHT = 1 << 19,
+			//
+		};
+		
+		enum class Format
+		{
+			UNKNOWN,
+			//
+			ETC1,
+			//
+			// ETC2 formats
+			RGB8,
+			SRGB8,
+			RGBA8,
+			SRGBA8,
+			R11,
+			SIGNED_R11,
+			RG11,
+			SIGNED_RG11,
+			RGB8A1,
+			SRGB8A1,
+			//
+			FORMATS,
+			//
+			DEFAULT = SRGB8
+		};
+
+		// constructor using source image
+		Image(float *a_pafSourceRGBA, unsigned int a_uiSourceWidth,
+				unsigned int a_uiSourceHeight,
+				ErrorMetric a_errormetric);
+
+		// constructor using encoding bits
+		Image(Format a_format, 
+				unsigned int a_uiSourceWidth, unsigned int a_uiSourceHeight,
+				unsigned char *a_paucEncidingBits, unsigned int a_uiEncodingBitsBytes,
+				Image *a_pimageSource,
+				ErrorMetric a_errormetric);
+
+		~Image(void);
+
+		EncodingStatus Encode(Format a_format, ErrorMetric a_errormetric, float a_fEffort, 
+			unsigned int a_uiJobs, unsigned int a_uiMaxJobs);
+
+		inline void AddToEncodingStatus(EncodingStatus a_encStatus)
+		{
+			m_encodingStatus = (EncodingStatus)((unsigned int)m_encodingStatus | (unsigned int)a_encStatus);
+		}
+		
+		inline unsigned int GetSourceWidth(void)
+		{
+			return m_uiSourceWidth;
+		}
+
+		inline unsigned int GetSourceHeight(void)
+		{
+			return m_uiSourceHeight;
+		}
+
+		inline unsigned int GetExtendedWidth(void)
+		{
+			return m_uiExtendedWidth;
+		}
+
+		inline unsigned int GetExtendedHeight(void)
+		{
+			return m_uiExtendedHeight;
+		}
+
+		inline unsigned int GetNumberOfBlocks()
+		{
+			return m_uiBlockColumns * m_uiBlockRows;
+		}
+
+		inline Block4x4 * GetBlocks()
+		{
+			return m_pablock;
+		}
+
+		inline unsigned char * GetEncodingBits(void)
+		{
+			return m_paucEncodingBits;
+		}
+
+		inline unsigned int GetEncodingBitsBytes(void)
+		{
+			return m_uiEncodingBitsBytes;
+		}
+
+		inline int GetEncodingTimeMs(void)
+		{
+			return m_iEncodeTime_ms;
+		}
+
+		float GetError(void);
+
+		inline ColorFloatRGBA * GetSourcePixel(unsigned int a_uiH, unsigned int a_uiV)
+		{
+			if (a_uiH >= m_uiSourceWidth || a_uiV >= m_uiSourceHeight)
+			{
+				return nullptr;
+			}
+
+			return &m_pafrgbaSource[a_uiV*m_uiSourceWidth + a_uiH];
+		}
+
+		inline Format GetFormat(void)
+		{
+			return m_format;
+		}
+
+		static Block4x4EncodingBits::Format DetermineEncodingBitsFormat(Format a_format);
+
+		inline static unsigned short CalcExtendedDimension(unsigned short a_ushOriginalDimension)
+		{
+			return (unsigned short)((a_ushOriginalDimension + 3) & ~3);
+		}
+
+		inline ErrorMetric GetErrorMetric(void)
+		{
+			return m_errormetric;
+		}
+
+		static const char * EncodingFormatToString(Image::Format a_format);
+		const char * EncodingFormatToString(void);
+		//used to get basic information about the image data
+		int m_iNumOpaquePixels;
+		int m_iNumTranslucentPixels;
+		int m_iNumTransparentPixels;
+
+		ColorFloatRGBA m_numColorValues;
+		ColorFloatRGBA m_numOutOfRangeValues;
+
+		bool m_bVerboseOutput;
+	private:
+		//add a warning or error to check for while encoding
+		inline void TrackEncodingWarning(EncodingStatus a_encStatus)
+		{
+			m_warningsToCapture = (EncodingStatus)((unsigned int)m_warningsToCapture | (unsigned int)a_encStatus);
+		}
+
+		//report the warning if it is something we care about for this encoding
+		inline void AddToEncodingStatusIfSignfigant(EncodingStatus a_encStatus)
+		{
+			if ((EncodingStatus)((unsigned int)m_warningsToCapture & (unsigned int)a_encStatus) == a_encStatus)
+			{
+				AddToEncodingStatus(a_encStatus);
+			}
+		}
+
+		Image(void);
+		void FindEncodingWarningTypesForCurFormat();
+		void FindAndSetEncodingWarnings();
+
+		void InitBlocksAndBlockSorter(void);
+
+		void RunFirstPass(unsigned int a_uiMultithreadingOffset, 
+							unsigned int a_uiMultithreadingStride);
+
+		void SetEncodingBits(unsigned int a_uiMultithreadingOffset,
+								unsigned int a_uiMultithreadingStride);
+
+		unsigned int IterateThroughWorstBlocks(unsigned int a_uiMaxBlocks,
+												unsigned int a_uiMultithreadingOffset,
+												unsigned int a_uiMultithreadingStride);
+
+		// inputs
+		ColorFloatRGBA *m_pafrgbaSource;
+		unsigned int m_uiSourceWidth;
+		unsigned int m_uiSourceHeight;
+		unsigned int m_uiExtendedWidth;
+		unsigned int m_uiExtendedHeight;
+		unsigned int m_uiBlockColumns;
+		unsigned int m_uiBlockRows;
+		// intermediate data
+		Block4x4 *m_pablock;
+		// encoding
+		Format m_format;
+		Block4x4EncodingBits::Format m_encodingbitsformat;
+		unsigned int m_uiEncodingBitsBytes;		// for entire image
+		unsigned char *m_paucEncodingBits;
+		ErrorMetric m_errormetric;
+		float m_fEffort;
+		// stats
+		int m_iEncodeTime_ms;
+		
+		SortedBlockList *m_psortedblocklist;
+		//this will hold any warning or errors that happen during encoding
+		EncodingStatus m_encodingStatus;
+		//these will be the warnings we are tracking
+		EncodingStatus m_warningsToCapture;
+	};
+
+} // namespace Etc

+ 85 - 0
thirdparty/etc2comp/EtcIndividualTrys.cpp

@@ -0,0 +1,85 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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.
+ */
+
+/*
+EtcIndividualTrys.cpp
+
+Gathers the results of the various encoding trys for both halves of a 4x4 block for Individual mode
+
+*/
+
+#include "EtcConfig.h"
+#include "EtcIndividualTrys.h"
+
+#include <assert.h>
+
+namespace Etc
+{
+
+	// ----------------------------------------------------------------------------------------------------
+	// construct a list of trys (encoding attempts)
+	//
+	// a_frgbaColor1 is the basecolor for the first half
+	// a_frgbaColor2 is the basecolor for the second half
+	// a_pauiPixelMapping1 is the pixel order for the first half
+	// a_pauiPixelMapping2 is the pixel order for the second half
+	// a_uiRadius is the amount to vary the base colors
+	//
+	IndividualTrys::IndividualTrys(ColorFloatRGBA a_frgbaColor1, ColorFloatRGBA a_frgbaColor2,
+									const unsigned int *a_pauiPixelMapping1,
+									const unsigned int *a_pauiPixelMapping2,
+									unsigned int a_uiRadius)
+	{
+		assert(a_uiRadius <= MAX_RADIUS);
+
+		ColorFloatRGBA frgbaQuantizedColor1 = a_frgbaColor1.QuantizeR4G4B4();
+		ColorFloatRGBA frgbaQuantizedColor2 = a_frgbaColor2.QuantizeR4G4B4();
+
+		// quantize base colors
+		// ensure that trys with a_uiRadius don't overflow
+		int iRed1 = MoveAwayFromEdge(frgbaQuantizedColor1.IntRed(15.0f), a_uiRadius);
+		int iGreen1 = MoveAwayFromEdge(frgbaQuantizedColor1.IntGreen(15.0f), a_uiRadius);
+		int iBlue1 = MoveAwayFromEdge(frgbaQuantizedColor1.IntBlue(15.0f), a_uiRadius);
+		int iRed2 = MoveAwayFromEdge(frgbaQuantizedColor2.IntRed(15.0f), a_uiRadius);
+		int iGreen2 = MoveAwayFromEdge(frgbaQuantizedColor2.IntGreen(15.0f), a_uiRadius);
+		int iBlue2 = MoveAwayFromEdge(frgbaQuantizedColor2.IntBlue(15.0f), a_uiRadius);
+
+		m_half1.Init(iRed1, iGreen1, iBlue1, a_pauiPixelMapping1, a_uiRadius);
+		m_half2.Init(iRed2, iGreen2, iBlue2, a_pauiPixelMapping2, a_uiRadius);
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+	void IndividualTrys::Half::Init(int a_iRed, int a_iGreen, int a_iBlue,
+									const unsigned int *a_pauiPixelMapping, unsigned int a_uiRadius)
+	{
+
+		m_iRed = a_iRed;
+		m_iGreen = a_iGreen;
+		m_iBlue = a_iBlue;
+
+		m_pauiPixelMapping = a_pauiPixelMapping;
+		m_uiRadius = a_uiRadius;
+
+		m_uiTrys = 0;
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+
+} // namespace Etc

+ 95 - 0
thirdparty/etc2comp/EtcIndividualTrys.h

@@ -0,0 +1,95 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include "EtcColorFloatRGBA.h"
+
+namespace Etc
+{
+
+	class IndividualTrys
+	{
+	public:
+
+		static const unsigned int MAX_RADIUS = 1;
+
+		IndividualTrys(ColorFloatRGBA a_frgbaColor1,
+						ColorFloatRGBA a_frgbaColor2,
+						const unsigned int *a_pauiPixelMapping1,
+						const unsigned int *a_pauiPixelMapping2,
+						unsigned int a_uiRadius);
+
+		inline static int MoveAwayFromEdge(int a_i, int a_iDistance)
+		{
+			if (a_i < (0+ a_iDistance))
+			{
+				return (0 + a_iDistance);
+			}
+			else if (a_i > (15- a_iDistance))
+			{
+				return (15 - a_iDistance);
+			}
+
+			return a_i;
+		}
+
+		class Try
+		{
+        public :
+			static const unsigned int SELECTORS = 8;	// per half
+
+			int m_iRed;
+			int m_iGreen;
+			int m_iBlue;
+			unsigned int m_uiCW;
+			unsigned int m_auiSelectors[SELECTORS];
+			float m_fError;
+        };
+
+		class Half
+		{
+		public:
+
+			static const unsigned int MAX_TRYS = 27;
+
+			void Init(int a_iRed, int a_iGreen, int a_iBlue, 
+						const unsigned int *a_pauiPixelMapping,
+						unsigned int a_uiRadius);
+
+			// center of trys
+			int m_iRed;
+			int m_iGreen;
+			int m_iBlue;
+
+			const unsigned int *m_pauiPixelMapping;
+			unsigned int m_uiRadius;
+
+			unsigned int m_uiTrys;
+			Try m_atry[MAX_TRYS];
+
+			Try *m_ptryBest;
+		};
+
+		Half m_half1;
+		Half m_half2;
+
+	};
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+
+} // namespace Etc

+ 64 - 0
thirdparty/etc2comp/EtcMath.cpp

@@ -0,0 +1,64 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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 "EtcConfig.h"
+#include "EtcMath.h"
+
+namespace Etc
+{
+
+	// ----------------------------------------------------------------------------------------------------
+	// calculate the line that best fits the set of XY points contained in a_afX[] and a_afY[]
+	// use a_fSlope and a_fOffset to define that line
+	//
+	bool Regression(float a_afX[], float a_afY[], unsigned int a_Points,
+					float *a_fSlope, float *a_fOffset)
+	{
+		float fPoints = (float)a_Points;
+
+		float fSumX = 0.0f;
+		float fSumY = 0.0f;
+		float fSumXY = 0.0f;
+		float fSumX2 = 0.0f;
+
+		for (unsigned int uiPoint = 0; uiPoint < a_Points; uiPoint++)
+		{
+			fSumX += a_afX[uiPoint];
+			fSumY += a_afY[uiPoint];
+			fSumXY += a_afX[uiPoint] * a_afY[uiPoint];
+			fSumX2 += a_afX[uiPoint] * a_afX[uiPoint];
+		}
+
+		float fDivisor = fPoints*fSumX2 - fSumX*fSumX;
+
+		// if vertical line
+		if (fDivisor == 0.0f)
+		{
+			*a_fSlope = 0.0f;
+			*a_fOffset = 0.0f;
+			return true;
+		}
+
+		*a_fSlope = (fPoints*fSumXY - fSumX*fSumY) / fDivisor;
+		*a_fOffset = (fSumY - (*a_fSlope)*fSumX) / fPoints;
+
+		return false;
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+
+} // namespace Etc

+ 40 - 0
thirdparty/etc2comp/EtcMath.h

@@ -0,0 +1,40 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <math.h>
+
+namespace Etc
+{
+
+	// ----------------------------------------------------------------------------------------------------
+	// return true if vertical line
+	bool Regression(float a_afX[], float a_afY[], unsigned int a_Points,
+					float *a_fSlope, float *a_fOffset);
+
+	inline float ConvertMSEToPSNR(float a_fMSE)
+	{
+		if (a_fMSE == 0.0f)
+		{
+			return INFINITY;
+		}
+
+		return 10.0f * log10f(1.0f / a_fMSE);
+	}
+
+
+}

+ 228 - 0
thirdparty/etc2comp/EtcSortedBlockList.cpp

@@ -0,0 +1,228 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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.
+ */
+
+/*
+EtcSortedBlockList.cpp
+
+SortedBlockList is a list of 4x4 blocks that can be used by the "effort" system to prioritize
+the encoding of the 4x4 blocks.
+
+The sorting is done with buckets, where each bucket is an indication of how much error each 4x4 block has
+
+*/
+
+#include "EtcConfig.h"
+#include "EtcSortedBlockList.h"
+
+#include "EtcBlock4x4.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+namespace Etc
+{
+
+	// ----------------------------------------------------------------------------------------------------
+	// construct an empty list
+	//
+	// allocate enough memory to add all of the image's 4x4 blocks later
+	// allocate enough buckets to sort the blocks
+	//
+	SortedBlockList::SortedBlockList(unsigned int a_uiImageBlocks, unsigned int a_uiBuckets)
+	{
+		m_uiImageBlocks = a_uiImageBlocks;
+		m_iBuckets = (int)a_uiBuckets;
+
+		m_uiAddedBlocks = 0;
+		m_uiSortedBlocks = 0;
+		m_palinkPool = new Link[m_uiImageBlocks];
+		m_pabucket = new Bucket[m_iBuckets];
+		m_fMaxError = 0.0f;
+
+		InitBuckets();
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	//
+	SortedBlockList::~SortedBlockList(void)
+	{
+		delete[] m_palinkPool;
+		delete[] m_pabucket;
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+    // add a 4x4 block to the list
+	// the 4x4 block will be sorted later
+	//
+    void SortedBlockList::AddBlock(Block4x4 *a_pblock)
+    {
+        assert(m_uiAddedBlocks < m_uiImageBlocks);
+        Link *plink = &m_palinkPool[m_uiAddedBlocks++];
+		plink->Init(a_pblock);
+    }
+
+	// ----------------------------------------------------------------------------------------------------
+	// sort all of the 4x4 blocks that have been added to the list
+	//
+	// first, determine the maximum error, then assign an error range to each bucket
+	// next, determine which bucket each 4x4 block belongs to based on the 4x4 block's error
+	// add the 4x4 block to the appropriate bucket
+	// lastly, walk thru the buckets and add each bucket to a sorted linked list
+	//
+	// the resultant sorting is an approximate sorting from most to least error
+	//
+    void SortedBlockList::Sort(void)
+    {
+		assert(m_uiAddedBlocks == m_uiImageBlocks);
+        InitBuckets();
+
+        // find max block error
+        m_fMaxError = -1.0f;
+
+        for (unsigned int uiLink = 0; uiLink < m_uiAddedBlocks; uiLink++)
+        {
+            Link *plinkBlock = &m_palinkPool[uiLink];
+
+            float fBlockError = plinkBlock->GetBlock()->GetError();
+            if (fBlockError > m_fMaxError)
+            {
+                m_fMaxError = fBlockError;
+            }
+        }
+        // prevent divide by zero or divide by negative
+        if (m_fMaxError <= 0.0f)
+        {
+            m_fMaxError = 1.0f;
+        }
+		//used for debugging
+		//int numDone = 0;
+        // put all of the blocks with unfinished encodings into the appropriate bucket
+		m_uiSortedBlocks = 0;
+        for (unsigned int uiLink = 0; uiLink < m_uiAddedBlocks; uiLink++)
+        {
+            Link *plinkBlock = &m_palinkPool[uiLink];
+
+			// if the encoding is done, don't add it to the list
+			if (plinkBlock->GetBlock()->GetEncoding()->IsDone())
+			{
+				//numDone++;
+				continue;
+			}
+
+            // calculate the appropriate sort bucket
+            float fBlockError = plinkBlock->GetBlock()->GetError();
+            int iBucket = (int) floorf(m_iBuckets * fBlockError / m_fMaxError);
+            // clamp to bucket index
+            iBucket = iBucket < 0 ? 0 : iBucket >= m_iBuckets ? m_iBuckets - 1 : iBucket;
+
+            // add block to bucket
+			{
+				Bucket *pbucket = &m_pabucket[iBucket];
+				if (pbucket->plinkLast)
+				{
+					pbucket->plinkLast->SetNext(plinkBlock);
+					pbucket->plinkLast = plinkBlock;
+				}
+				else
+				{
+					pbucket->plinkFirst = pbucket->plinkLast = plinkBlock;
+				}
+				plinkBlock->SetNext(nullptr);
+			}
+
+			m_uiSortedBlocks++;
+
+            if (0)
+            {
+                printf("%u: e=%.3f\n", uiLink, fBlockError);
+                Print();
+                printf("\n\n\n");
+            }
+        }
+		//printf("num blocks already done: %d\n",numDone);
+		//link the blocks together across buckets
+		m_plinkFirst = nullptr;
+		m_plinkLast = nullptr;
+		for (int iBucket = m_iBuckets - 1; iBucket >= 0; iBucket--)
+		{
+			Bucket *pbucket = &m_pabucket[iBucket];
+
+			if (pbucket->plinkFirst)
+			{
+				if (m_plinkFirst == nullptr)
+				{
+					m_plinkFirst = pbucket->plinkFirst;
+				}
+				else
+				{
+					assert(pbucket->plinkLast->GetNext() == nullptr);
+					m_plinkLast->SetNext(pbucket->plinkFirst);
+				}
+
+				m_plinkLast = pbucket->plinkLast;
+			}
+		}
+
+
+	}
+
+	// ----------------------------------------------------------------------------------------------------
+	// clear all of the buckets.  normally done in preparation for a sort
+	//
+	void SortedBlockList::InitBuckets(void)
+    {
+        for (int iBucket = 0; iBucket < m_iBuckets; iBucket++)
+        {
+            Bucket *pbucket = &m_pabucket[iBucket];
+
+            pbucket->plinkFirst = 0;
+            pbucket->plinkLast = 0;
+        }
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+    // print out the list of sorted 4x4 blocks
+	// normally used for debugging
+	//
+    void SortedBlockList::Print(void)
+    {
+        for (int iBucket = m_iBuckets-1; iBucket >= 0; iBucket--)
+        {
+            Bucket *pbucket = &m_pabucket[iBucket];
+
+            unsigned int uiBlocks = 0;
+            for (Link *plink = pbucket->plinkFirst; plink != nullptr; plink = plink->GetNext() )
+            {
+                uiBlocks++;
+
+				if (plink == pbucket->plinkLast)
+				{
+					break;
+				}
+            }
+
+            float fBucketError = m_fMaxError * iBucket / m_iBuckets;
+            float fBucketRMS = sqrtf(fBucketError / (4.0f*16.0f) );
+            printf("%3d: e=%.3f rms=%.6f %u\n", iBucket, fBucketError, fBucketRMS, uiBlocks);
+        }
+    }
+
+    // ----------------------------------------------------------------------------------------------------
+    //
+
+}   // namespace Etc

+ 124 - 0
thirdparty/etc2comp/EtcSortedBlockList.h

@@ -0,0 +1,124 @@
+/*
+ * Copyright 2015 The Etc2Comp Authors.
+ *
+ * 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.
+ */
+
+#pragma once
+
+namespace Etc
+{
+	class Block4x4;
+
+    class SortedBlockList
+    {
+    public:
+
+		class Link
+		{
+		public:
+
+			inline void Init(Block4x4 *a_pblock)
+			{
+				m_pblock = a_pblock;
+				m_plinkNext = nullptr;
+			}
+
+			inline Block4x4 * GetBlock(void)
+			{
+				return m_pblock;
+			}
+
+			inline void SetNext(Link *a_plinkNext)
+			{
+				m_plinkNext = a_plinkNext;
+			}
+
+			inline Link * GetNext(void)
+			{
+				return m_plinkNext;
+			}
+
+			inline Link * Advance(unsigned int a_uiSteps = 1)
+			{
+				Link *plink = this;
+
+				for (unsigned int uiStep = 0; uiStep < a_uiSteps; uiStep++)
+				{
+					if (plink == nullptr)
+					{
+						break;
+					}
+
+					plink = plink->m_plinkNext;
+				}
+
+				return plink;
+			}
+
+		private:
+
+			Block4x4 *m_pblock;
+			Link *m_plinkNext;
+		};
+
+		SortedBlockList(unsigned int a_uiImageBlocks, unsigned int a_uiBuckets);
+		~SortedBlockList(void);
+
+        void AddBlock(Block4x4 *a_pblock);
+
+        void Sort(void);
+
+		inline Link * GetLinkToFirstBlock(void)
+		{
+			return m_plinkFirst;
+		}
+
+		inline unsigned int GetNumberOfAddedBlocks(void)
+		{
+			return m_uiAddedBlocks;
+		}
+
+		inline unsigned int GetNumberOfSortedBlocks(void)
+		{
+			return m_uiSortedBlocks;
+		}
+
+		void Print(void);
+
+	private:
+
+        void InitBuckets(void);
+
+        class Bucket
+        {
+        public:
+            Link *plinkFirst;
+            Link *plinkLast;
+        };
+
+        unsigned int m_uiImageBlocks;
+        int m_iBuckets;
+
+		unsigned int m_uiAddedBlocks;
+		unsigned int m_uiSortedBlocks;
+		Link *m_palinkPool;
+        Bucket *m_pabucket;
+        float m_fMaxError;
+
+		Link *m_plinkFirst;
+		Link *m_plinkLast;
+
+    };
+
+} // namespace Etc

+ 202 - 0
thirdparty/etc2comp/LICENSE

@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.

+ 197 - 0
thirdparty/etc2comp/README.md

@@ -0,0 +1,197 @@
+# Etc2Comp - Texture to ETC2 compressor
+
+Etc2Comp is a command line tool that converts textures (e.g. bitmaps)
+into the [ETC2](https://en.wikipedia.org/wiki/Ericsson_Texture_Compression)
+format. The tool is built with a focus on encoding performance
+to reduce the amount of time required to compile asset heavy applications as
+well as reduce overall application size.
+
+This repo provides source code that can be compiled into a binary. The
+binary can then be used to convert textures to the ETC2 format.
+
+Important: This is not an official Google product. It is an experimental
+library published as-is. Please see the CONTRIBUTORS.md file for information
+about questions or issues.
+
+## Setup
+This project uses [CMake](https://cmake.org/) to generate platform-specific
+build files:
+ - Linux: make files
+ - OS X: Xcode workspace files
+ - Microsoft Windows: Visual Studio solution files
+ - Note: CMake supports other formats, but this doc only provides steps for
+ one of each platform for brevity.
+
+Refer to each platform's setup section to setup your environment and build
+an Etc2Comp binary. Then skip to the usage section of this page for examples
+of how to use the library.
+
+### Setup for OS X
+ build tested on this config:
+  OS X 10.9.5 i7 16GB RAM
+  Xcode 5.1.1
+  cmake 3.2.3
+  
+Start by downloading and installing the following components if they are not
+already installed on your development machine.
+ - *Xcode* version 5.1.1, or greater
+ - [CMake](https://cmake.org/download/) version 3.2.3, or greater
+
+To build the Etc2Comp binary:
+ 1. Open a *Terminal* window and navigate to the project directory.
+ 1. Run `mkdir build_xcode`
+ 1. Run `cd build_xcode`
+ 1. Run `cmake -G Xcode ../`
+ 1. Open *Xcode* and import the `build_xcode/EtcTest.xcodeproj` file.
+ 1. Open the Product menu and choose Build For -> Running.
+ 1. Once the build succeeds the binary located at `build_xcode/EtcTool/Debug/EtcTool`
+can be executed.
+
+Optional
+Xcode EtcTool ‘Run’ preferences
+note: if the build_xcode/EtcTest.xcodeproj is manually deleted then some Xcode preferences 
+will need to be set by hand after cmake is run (these prefs are retained across 
+cmake updates if the .xcodeproj is not deleted/removed)
+
+1. Set the active scheme to ‘EtcTool’
+1. Edit the scheme
+1. Select option ‘Run EtcTool’, then tab ‘Arguments’. 
+Add this launch argument: ‘-argfile ../../EtcTool/args.txt’
+1. Select tab ‘Options’ and set a custom working directory to: ‘$(SRCROOT)/Build_Xcode/EtcTool’
+
+### SetUp for Windows
+
+1. Open a *Terminal* window and navigate to the project directory.
+1. Run `mkdir build_vs`
+1. Run `cd build_vs`
+1. Run CMAKE, noting what build version you need, and pointing to the parent directory as the source root; 
+  For VS 2013 : `cmake -G "Visual Studio 12 2013 Win64" ../`
+  For VS 2015 : `cmake -G "Visual Studio 14 2015 Win64" ../`
+  NOTE: To see what supported Visual Studio outputs there are, run `cmake -G`
+1. open the 'EtcTest' solution
+1. make the 'EtcTool' project the start up project 
+1. (optional) in the project properties, under 'Debugging ->command arguments' 
+add the argfile textfile thats included in the EtcTool directory. 
+example: -argfile C:\etc2\EtcTool\Args.txt
+
+### Setup For Linux
+The Linux build was tested on this config:
+  Ubuntu desktop 14.04
+  gcc/g++ 4.8
+  cmake 2.8.12.2
+
+1. Verify linux has cmake and C++-11 capable g++ installed
+1. Open shell
+1. Run `mkdir build_linux`
+1. Run `cd build_linux`
+1. Run `cmake ../`
+1. Run `make`
+1. navigate to the newly created EtcTool directory `cd EtcTool`
+1. run the executable: `./EtcTool -argfile ../../EtcTool/args.txt`
+
+Skip to the <a href="#usage">Usage</a> section for more information about using the
+tool.
+
+## Usage
+
+### Command Line Usage
+EtcTool can be run from the command line with the following usage:
+    etctool.exe source_image [options ...] -output encoded_image
+
+The encoder will use an array of RGBA floats read from the source_image to create 
+an ETC1 or ETC2 encoded image in encoded_image.  The RGBA floats should be in the 
+range [0:1].
+
+Options:
+
+    -analyze <analysis_folder>
+    -argfile <arg_file>           additional command line arguments read from a file
+    -blockAtHV <H V>              encodes a single block that contains the
+                                  pixel specified by the H V coordinates
+    -compare <comparison_image>   compares source_image to comparison_image
+    -effort <amount>              number between 0 and 100 to specify the encoding quality 
+                                  (100 is the highest quality)
+    -errormetric <error_metric>   specify the error metric, the options are
+                                  rgba, rgbx, rec709, numeric and normalxyz
+    -format <etc_format>          ETC1, RGB8, SRGB8, RGBA8, SRGB8, RGB8A1,
+                                  SRGB8A1 or R11
+    -help                         prints this message
+    -jobs or -j <thread_count>    specifies the number of threads (default=1)
+    -normalizexyz                 normalize RGB to have a length of 1
+    -verbose or -v                shows status information during the encoding
+                                  process
+	-mipmaps or -m <mip_count>    sets the maximum number of mipaps to generate (default=1)
+	-mipwrap or -w <x|y|xy>       sets the mipmap filter wrap mode (default=clamp)
+
+* -analyze will run an analysis of the encoding and place it in folder 
+"analysis_folder" (e.g. ../analysis/kodim05).  within the analysis_folder, a folder 
+will be created with a name of the current date/time (e.g. 20151204_153306).  this 
+date/time folder is used to compare encodings of the same texture over time.  
+within the date/time folder is a text file with several encoding stats and a 2x png 
+image showing the encoding mode for each 4x4 block.
+
+* -argfile allows additional command line arguments to be placed in a text file
+
+* -blockAtHV selects the 4x4 pixel subset of the source image at position (H,V).  
+This is mainly used for debugging
+
+* -compare compares the source image to the created encoded image. The encoding
+will dictate what error analysis is used in the comparison.
+
+* -effort uses an "amount" between 0 and 100 to determine how much additional effort 
+to apply during the encoding.
+
+* -errormetric selects the fitting algorithm used by the encoder.  "rgba" calculates 
+RMS error using RGB components that are weighted by A.  "rgbx" calculates RMS error 
+using RGBA components, where A is treated as an additional data channel, instead of 
+as alpha.  "rec709" is similar to "rgba", except the RGB components are also weighted 
+according to Rec709.  "numeric" calculates RMS error using unweighted RGBA components.  
+"normalize" calculates error based on dot product and vector length for RGB and RMS 
+error for A.
+
+* -help prints out the usage message
+
+* -jobs enables multi-threading to speed up image encoding
+
+* -normalizexyz normalizes the source RGB to have a length of 1.
+
+* -verbose shows information on the current encoding process. It will then display the 
+PSNR and time time it took to encode the image.
+
+* -mipmaps takes an argument that specifies how many mipmaps to generate from the 
+source image.  The mipmaps are generated with a lanczos3 filter using edge clamping.
+If the mipmaps option is not specified no mipmaps are created.
+
+* -mipwrap takes an argument that specifies the mipmap filter wrap mode.  The options 
+are "x", "y" and "xy" which specify wrapping in x only, y only or x and y respectively.
+The default options are clamping in both x and y.
+
+Note: Path names can use slashes or backslashes.  The tool will convert the 
+slashes to the appropriate polarity for the current platform.
+
+
+## API
+
+The library supports two different APIs - a C-like API that is not heavily 
+class-based and a class-based API.
+
+main() in EtcTool.cpp contains an example of both APIs.
+
+The Encode() method now returns an EncodingStatus that contains bit flags for
+reporting various warnings and flags encountered when encoding.
+
+
+## Copyright
+Copyright 2015 Etc2Comp Authors.
+
+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.

+ 0 - 2446
thirdparty/rg-etc1/rg_etc1.cpp

@@ -1,2446 +0,0 @@
-// File: rg_etc1.cpp - Fast, high quality ETC1 block packer/unpacker - Rich Geldreich <[email protected]>
-// Please see ZLIB license at the end of rg_etc1.h.
-//
-// For more information Ericsson Texture Compression (ETC/ETC1), see:
-// http://www.khronos.org/registry/gles/extensions/OES/OES_compressed_ETC1_RGB8_texture.txt
-//
-// v1.04 - 5/15/14 - Fix signed vs. unsigned subtraction problem (noticed when compiled with gcc) in pack_etc1_block_init(). 
-//         This issue would cause an assert when this func. was called in debug. (Note this module was developed/testing with MSVC, 
-//         I still need to test it throughly when compiled with gcc.)
-//
-// v1.03 - 5/12/13 - Initial public release
-#include "rg_etc1.h"
-
-#include <stdlib.h>
-#include <memory.h>
-#include <assert.h>
-//#include <stdio.h>
-#include <math.h>
-
-#pragma warning (disable: 4201) //  nonstandard extension used : nameless struct/union
-
-#if defined(_DEBUG) || defined(DEBUG)
-#define RG_ETC1_BUILD_DEBUG
-#endif
-
-#define RG_ETC1_ASSERT assert
-
-namespace rg_etc1
-{
-   typedef unsigned char uint8;
-   typedef unsigned short uint16;
-   typedef unsigned int uint;
-   typedef unsigned int uint32;
-   typedef long long int64;
-   typedef unsigned long long uint64;
-
-   const uint32 cUINT32_MAX = 0xFFFFFFFFU;
-   const uint64 cUINT64_MAX = 0xFFFFFFFFFFFFFFFFULL; //0xFFFFFFFFFFFFFFFFui64;
-   
-   template<typename T> inline T minimum(T a, T b) { return (a < b) ? a : b; }
-   template<typename T> inline T minimum(T a, T b, T c) { return minimum(minimum(a, b), c); }
-   template<typename T> inline T maximum(T a, T b) { return (a > b) ? a : b; }
-   template<typename T> inline T maximum(T a, T b, T c) { return maximum(maximum(a, b), c); }
-   template<typename T> inline T clamp(T value, T low, T high) { return (value < low) ? low : ((value > high) ? high : value); }
-   template<typename T> inline T square(T value) { return value * value; }
-   template<typename T> inline void zero_object(T& obj) { memset((void*)&obj, 0, sizeof(obj)); }
-   template<typename T> inline void zero_this(T* pObj) { memset((void*)pObj, 0, sizeof(*pObj)); }
-
-   template<class T, size_t N> T decay_array_to_subtype(T (&a)[N]);   
-
-#define RG_ETC1_ARRAY_SIZE(X) (sizeof(X) / sizeof(decay_array_to_subtype(X)))
-
-   enum eNoClamp { cNoClamp };
-
-   struct color_quad_u8
-   {
-      static inline int clamp(int v) { if (v & 0xFFFFFF00U) v = (~(static_cast<int>(v) >> 31)) & 0xFF; return v; }
-
-      struct component_traits { enum { cSigned = false, cFloat = false, cMin = 0U, cMax = 255U }; };
-
-   public:
-      typedef unsigned char component_t;
-      typedef int parameter_t;
-
-      enum { cNumComps = 4 };
-
-      union
-      {
-         struct
-         {
-            component_t r;
-            component_t g;
-            component_t b;
-            component_t a;
-         };
-
-         component_t c[cNumComps];
-
-         uint32 m_u32;
-      };
-
-      inline color_quad_u8()
-      {
-      }
-
-      inline color_quad_u8(const color_quad_u8& other) : m_u32(other.m_u32)
-      {
-      }
-
-      explicit inline color_quad_u8(parameter_t y, parameter_t alpha = component_traits::cMax)
-      {
-         set(y, alpha);
-      }
-
-      inline color_quad_u8(parameter_t red, parameter_t green, parameter_t blue, parameter_t alpha = component_traits::cMax)
-      {
-         set(red, green, blue, alpha);
-      }
-
-      explicit inline color_quad_u8(eNoClamp, parameter_t y, parameter_t alpha = component_traits::cMax)
-      {
-         set_noclamp_y_alpha(y, alpha);
-      }
-
-      inline color_quad_u8(eNoClamp, parameter_t red, parameter_t green, parameter_t blue, parameter_t alpha = component_traits::cMax)
-      {
-         set_noclamp_rgba(red, green, blue, alpha);
-      }
-
-      inline void clear()
-      {
-         m_u32 = 0;
-      }
-
-      inline color_quad_u8& operator= (const color_quad_u8& other)
-      {
-         m_u32 = other.m_u32;
-         return *this;
-      }
-
-      inline color_quad_u8& set_rgb(const color_quad_u8& other)
-      {
-         r = other.r;
-         g = other.g;
-         b = other.b;
-         return *this;
-      }
-
-      inline color_quad_u8& operator= (parameter_t y)
-      {
-         set(y, component_traits::cMax);
-         return *this;
-      }
-
-      inline color_quad_u8& set(parameter_t y, parameter_t alpha = component_traits::cMax)
-      {
-         y = clamp(y);
-         alpha = clamp(alpha);
-         r = static_cast<component_t>(y);
-         g = static_cast<component_t>(y);
-         b = static_cast<component_t>(y);
-         a = static_cast<component_t>(alpha);
-         return *this;
-      }
-
-      inline color_quad_u8& set_noclamp_y_alpha(parameter_t y, parameter_t alpha = component_traits::cMax)
-      {
-         RG_ETC1_ASSERT( (y >= component_traits::cMin) && (y <= component_traits::cMax) );
-         RG_ETC1_ASSERT( (alpha >= component_traits::cMin) && (alpha <= component_traits::cMax) );
-
-         r = static_cast<component_t>(y);
-         g = static_cast<component_t>(y);
-         b = static_cast<component_t>(y);
-         a = static_cast<component_t>(alpha);
-         return *this;
-      }
-
-      inline color_quad_u8& set(parameter_t red, parameter_t green, parameter_t blue, parameter_t alpha = component_traits::cMax)
-      {
-         r = static_cast<component_t>(clamp(red));
-         g = static_cast<component_t>(clamp(green));
-         b = static_cast<component_t>(clamp(blue));
-         a = static_cast<component_t>(clamp(alpha));
-         return *this;
-      }
-
-      inline color_quad_u8& set_noclamp_rgba(parameter_t red, parameter_t green, parameter_t blue, parameter_t alpha)
-      {
-         RG_ETC1_ASSERT( (red >= component_traits::cMin) && (red <= component_traits::cMax) );
-         RG_ETC1_ASSERT( (green >= component_traits::cMin) && (green <= component_traits::cMax) );
-         RG_ETC1_ASSERT( (blue >= component_traits::cMin) && (blue <= component_traits::cMax) );
-         RG_ETC1_ASSERT( (alpha >= component_traits::cMin) && (alpha <= component_traits::cMax) );
-
-         r = static_cast<component_t>(red);
-         g = static_cast<component_t>(green);
-         b = static_cast<component_t>(blue);
-         a = static_cast<component_t>(alpha);
-         return *this;
-      }
-
-      inline color_quad_u8& set_noclamp_rgb(parameter_t red, parameter_t green, parameter_t blue)
-      {
-         RG_ETC1_ASSERT( (red >= component_traits::cMin) && (red <= component_traits::cMax) );
-         RG_ETC1_ASSERT( (green >= component_traits::cMin) && (green <= component_traits::cMax) );
-         RG_ETC1_ASSERT( (blue >= component_traits::cMin) && (blue <= component_traits::cMax) );
-
-         r = static_cast<component_t>(red);
-         g = static_cast<component_t>(green);
-         b = static_cast<component_t>(blue);
-         return *this;
-      }
-
-      static inline parameter_t get_min_comp() { return component_traits::cMin; }
-      static inline parameter_t get_max_comp() { return component_traits::cMax; }
-      static inline bool get_comps_are_signed() { return component_traits::cSigned; }
-
-      inline component_t operator[] (uint i) const { RG_ETC1_ASSERT(i < cNumComps); return c[i]; }
-      inline component_t& operator[] (uint i) { RG_ETC1_ASSERT(i < cNumComps); return c[i]; }
-
-      inline color_quad_u8& set_component(uint i, parameter_t f)
-      {
-         RG_ETC1_ASSERT(i < cNumComps);
-
-         c[i] = static_cast<component_t>(clamp(f));
-
-         return *this;
-      }
-
-      inline color_quad_u8& set_grayscale(parameter_t l)
-      {
-         component_t x = static_cast<component_t>(clamp(l));
-         c[0] = x;
-         c[1] = x;
-         c[2] = x;
-         return *this;
-      }
-
-      inline color_quad_u8& clamp(const color_quad_u8& l, const color_quad_u8& h)
-      {
-         for (uint i = 0; i < cNumComps; i++)
-            c[i] = static_cast<component_t>(rg_etc1::clamp<parameter_t>(c[i], l[i], h[i]));
-         return *this;
-      }
-
-      inline color_quad_u8& clamp(parameter_t l, parameter_t h)
-      {
-         for (uint i = 0; i < cNumComps; i++)
-            c[i] = static_cast<component_t>(rg_etc1::clamp<parameter_t>(c[i], l, h));
-         return *this;
-      }
-
-      // Returns CCIR 601 luma (consistent with color_utils::RGB_To_Y).
-      inline parameter_t get_luma() const
-      {
-         return static_cast<parameter_t>((19595U * r + 38470U * g + 7471U * b + 32768U) >> 16U);
-      }
-
-      // Returns REC 709 luma.
-      inline parameter_t get_luma_rec709() const
-      {
-         return static_cast<parameter_t>((13938U * r + 46869U * g + 4729U * b + 32768U) >> 16U);
-      }
-
-      inline uint squared_distance_rgb(const color_quad_u8& c) const
-      {
-         return rg_etc1::square(r - c.r) + rg_etc1::square(g - c.g) + rg_etc1::square(b - c.b);
-      }
-
-      inline uint squared_distance_rgba(const color_quad_u8& c) const
-      {
-         return rg_etc1::square(r - c.r) + rg_etc1::square(g - c.g) + rg_etc1::square(b - c.b) + rg_etc1::square(a - c.a);
-      }
-
-      inline bool rgb_equals(const color_quad_u8& rhs) const
-      {
-         return (r == rhs.r) && (g == rhs.g) && (b == rhs.b);
-      }
-
-      inline bool operator== (const color_quad_u8& rhs) const
-      {
-         return m_u32 == rhs.m_u32;
-      }
-
-      color_quad_u8& operator+= (const color_quad_u8& other)
-      {
-         for (uint i = 0; i < 4; i++)
-            c[i] = static_cast<component_t>(clamp(c[i] + other.c[i]));
-         return *this;
-      }
-
-      color_quad_u8& operator-= (const color_quad_u8& other)
-      {
-         for (uint i = 0; i < 4; i++)
-            c[i] = static_cast<component_t>(clamp(c[i] - other.c[i]));
-         return *this;
-      }
-
-      friend color_quad_u8 operator+ (const color_quad_u8& lhs, const color_quad_u8& rhs)
-      {
-         color_quad_u8 result(lhs);
-         result += rhs;
-         return result;
-      }
-
-      friend color_quad_u8 operator- (const color_quad_u8& lhs, const color_quad_u8& rhs)
-      {
-         color_quad_u8 result(lhs);
-         result -= rhs;
-         return result;
-      }
-   }; // class color_quad_u8
-
-   struct vec3F
-   {
-      float m_s[3];
-      
-      inline vec3F() { }
-      inline vec3F(float s) { m_s[0] = s; m_s[1] = s; m_s[2] = s; }
-      inline vec3F(float x, float y, float z) { m_s[0] = x; m_s[1] = y; m_s[2] = z; }
-      
-      inline float operator[] (uint i) const { RG_ETC1_ASSERT(i < 3); return m_s[i]; }
-
-      inline vec3F& operator += (const vec3F& other) { for (uint i = 0; i < 3; i++) m_s[i] += other.m_s[i]; return *this; }
-
-      inline vec3F& operator *= (float s) { for (uint i = 0; i < 3; i++) m_s[i] *= s; return *this; }
-   };
-     
-   enum etc_constants
-   {
-      cETC1BytesPerBlock = 8U,
-
-      cETC1SelectorBits = 2U,
-      cETC1SelectorValues = 1U << cETC1SelectorBits,
-      cETC1SelectorMask = cETC1SelectorValues - 1U,
-
-      cETC1BlockShift = 2U,
-      cETC1BlockSize = 1U << cETC1BlockShift,
-
-      cETC1LSBSelectorIndicesBitOffset = 0,
-      cETC1MSBSelectorIndicesBitOffset = 16,
-
-      cETC1FlipBitOffset = 32,
-      cETC1DiffBitOffset = 33,
-
-      cETC1IntenModifierNumBits = 3,
-      cETC1IntenModifierValues = 1 << cETC1IntenModifierNumBits,
-      cETC1RightIntenModifierTableBitOffset = 34,
-      cETC1LeftIntenModifierTableBitOffset = 37,
-
-      // Base+Delta encoding (5 bit bases, 3 bit delta)
-      cETC1BaseColorCompNumBits = 5,
-      cETC1BaseColorCompMax = 1 << cETC1BaseColorCompNumBits,
-
-      cETC1DeltaColorCompNumBits = 3,
-      cETC1DeltaColorComp = 1 << cETC1DeltaColorCompNumBits,
-      cETC1DeltaColorCompMax = 1 << cETC1DeltaColorCompNumBits,
-
-      cETC1BaseColor5RBitOffset = 59,
-      cETC1BaseColor5GBitOffset = 51,
-      cETC1BaseColor5BBitOffset = 43,
-
-      cETC1DeltaColor3RBitOffset = 56,
-      cETC1DeltaColor3GBitOffset = 48,
-      cETC1DeltaColor3BBitOffset = 40,
-
-      // Absolute (non-delta) encoding (two 4-bit per component bases)
-      cETC1AbsColorCompNumBits = 4,
-      cETC1AbsColorCompMax = 1 << cETC1AbsColorCompNumBits,
-
-      cETC1AbsColor4R1BitOffset = 60,
-      cETC1AbsColor4G1BitOffset = 52,
-      cETC1AbsColor4B1BitOffset = 44,
-
-      cETC1AbsColor4R2BitOffset = 56,
-      cETC1AbsColor4G2BitOffset = 48,
-      cETC1AbsColor4B2BitOffset = 40,
-
-      cETC1ColorDeltaMin = -4,
-      cETC1ColorDeltaMax = 3,
-
-      // Delta3:
-      // 0   1   2   3   4   5   6   7
-      // 000 001 010 011 100 101 110 111
-      // 0   1   2   3   -4  -3  -2  -1
-   };
-   
-   static uint8 g_quant5_tab[256+16];
-
-   static const int g_etc1_inten_tables[cETC1IntenModifierValues][cETC1SelectorValues] = 
-   { 
-      { -8,  -2,   2,   8 }, { -17,  -5,  5,  17 }, { -29,  -9,   9,  29 }, {  -42, -13, 13,  42 }, 
-      { -60, -18, 18,  60 }, { -80, -24, 24,  80 }, { -106, -33, 33, 106 }, { -183, -47, 47, 183 } 
-   };
-
-   static const uint8 g_etc1_to_selector_index[cETC1SelectorValues] = { 2, 3, 1, 0 };
-   static const uint8 g_selector_index_to_etc1[cETC1SelectorValues] = { 3, 2, 0, 1 };
-      
-   // Given an ETC1 diff/inten_table/selector, and an 8-bit desired color, this table encodes the best packed_color in the low byte, and the abs error in the high byte.
-   static uint16 g_etc1_inverse_lookup[2*8*4][256];      // [diff/inten_table/selector][desired_color]
-
-   // g_color8_to_etc_block_config[color][table_index] = Supplies for each 8-bit color value a list of packed ETC1 diff/intensity table/selectors/packed_colors that map to that color.
-   // To pack: diff | (inten << 1) | (selector << 4) | (packed_c << 8)
-   static const uint16 g_color8_to_etc_block_config_0_255[2][33] =
-   {
-      { 0x0000,  0x0010,  0x0002,  0x0012,  0x0004,  0x0014,  0x0006,  0x0016,  0x0008,  0x0018,  0x000A,  0x001A,  0x000C,  0x001C,  0x000E,  0x001E,
-        0x0001,  0x0011,  0x0003,  0x0013,  0x0005,  0x0015,  0x0007,  0x0017,  0x0009,  0x0019,  0x000B,  0x001B,  0x000D,  0x001D,  0x000F,  0x001F, 0xFFFF },
-      { 0x0F20,  0x0F30,  0x0E32,  0x0F22,  0x0E34,  0x0F24,  0x0D36,  0x0F26,  0x0C38,  0x0E28,  0x0B3A,  0x0E2A,  0x093C,  0x0E2C,  0x053E,  0x0D2E,
-        0x1E31,  0x1F21,  0x1D33,  0x1F23,  0x1C35,  0x1E25,  0x1A37,  0x1E27,  0x1839,  0x1D29,  0x163B,  0x1C2B,  0x133D,  0x1B2D,  0x093F,  0x1A2F, 0xFFFF },
-   };
-
-   // Really only [254][11].
-   static const uint16 g_color8_to_etc_block_config_1_to_254[254][12] = 
-   {
-      { 0x021C, 0x0D0D, 0xFFFF }, { 0x0020, 0x0021, 0x0A0B, 0x061F, 0xFFFF }, { 0x0113, 0x0217, 0xFFFF }, { 0x0116, 0x031E,
-      0x0B0E, 0x0405, 0xFFFF }, { 0x0022, 0x0204, 0x050A, 0x0023, 0xFFFF }, { 0x0111, 0x0319, 0x0809, 0x170F, 0xFFFF }, {
-      0x0303, 0x0215, 0x0607, 0xFFFF }, { 0x0030, 0x0114, 0x0408, 0x0031, 0x0201, 0x051D, 0xFFFF }, { 0x0100, 0x0024, 0x0306,
-      0x0025, 0x041B, 0x0E0D, 0xFFFF }, { 0x021A, 0x0121, 0x0B0B, 0x071F, 0xFFFF }, { 0x0213, 0x0317, 0xFFFF }, { 0x0112,
-      0x0505, 0xFFFF }, { 0x0026, 0x070C, 0x0123, 0x0027, 0xFFFF }, { 0x0211, 0x0909, 0xFFFF }, { 0x0110, 0x0315, 0x0707,
-      0x0419, 0x180F, 0xFFFF }, { 0x0218, 0x0131, 0x0301, 0x0403, 0x061D, 0xFFFF }, { 0x0032, 0x0202, 0x0033, 0x0125, 0x051B,
-      0x0F0D, 0xFFFF }, { 0x0028, 0x031C, 0x0221, 0x0029, 0xFFFF }, { 0x0120, 0x0313, 0x0C0B, 0x081F, 0xFFFF }, { 0x0605,
-      0x0417, 0xFFFF }, { 0x0216, 0x041E, 0x0C0E, 0x0223, 0x0127, 0xFFFF }, { 0x0122, 0x0304, 0x060A, 0x0311, 0x0A09, 0xFFFF
-      }, { 0x0519, 0x190F, 0xFFFF }, { 0x002A, 0x0231, 0x0503, 0x0415, 0x0807, 0x002B, 0x071D, 0xFFFF }, { 0x0130, 0x0214,
-      0x0508, 0x0401, 0x0133, 0x0225, 0x061B, 0xFFFF }, { 0x0200, 0x0124, 0x0406, 0x0321, 0x0129, 0x100D, 0xFFFF }, { 0x031A,
-      0x0D0B, 0x091F, 0xFFFF }, { 0x0413, 0x0705, 0x0517, 0xFFFF }, { 0x0212, 0x0034, 0x0323, 0x0035, 0x0227, 0xFFFF }, {
-      0x0126, 0x080C, 0x0B09, 0xFFFF }, { 0x0411, 0x0619, 0x1A0F, 0xFFFF }, { 0x0210, 0x0331, 0x0603, 0x0515, 0x0907, 0x012B,
-      0xFFFF }, { 0x0318, 0x002C, 0x0501, 0x0233, 0x0325, 0x071B, 0x002D, 0x081D, 0xFFFF }, { 0x0132, 0x0302, 0x0229, 0x110D,
-      0xFFFF }, { 0x0128, 0x041C, 0x0421, 0x0E0B, 0x0A1F, 0xFFFF }, { 0x0220, 0x0513, 0x0617, 0xFFFF }, { 0x0135, 0x0805,
-      0x0327, 0xFFFF }, { 0x0316, 0x051E, 0x0D0E, 0x0423, 0xFFFF }, { 0x0222, 0x0404, 0x070A, 0x0511, 0x0719, 0x0C09, 0x1B0F,
-      0xFFFF }, { 0x0703, 0x0615, 0x0A07, 0x022B, 0xFFFF }, { 0x012A, 0x0431, 0x0601, 0x0333, 0x012D, 0x091D, 0xFFFF }, {
-      0x0230, 0x0314, 0x0036, 0x0608, 0x0425, 0x0037, 0x0329, 0x081B, 0x120D, 0xFFFF }, { 0x0300, 0x0224, 0x0506, 0x0521,
-      0x0F0B, 0x0B1F, 0xFFFF }, { 0x041A, 0x0613, 0x0717, 0xFFFF }, { 0x0235, 0x0905, 0xFFFF }, { 0x0312, 0x0134, 0x0523,
-      0x0427, 0xFFFF }, { 0x0226, 0x090C, 0x002E, 0x0611, 0x0D09, 0x002F, 0xFFFF }, { 0x0715, 0x0B07, 0x0819, 0x032B, 0x1C0F,
-      0xFFFF }, { 0x0310, 0x0531, 0x0701, 0x0803, 0x022D, 0x0A1D, 0xFFFF }, { 0x0418, 0x012C, 0x0433, 0x0525, 0x0137, 0x091B,
-      0x130D, 0xFFFF }, { 0x0232, 0x0402, 0x0621, 0x0429, 0xFFFF }, { 0x0228, 0x051C, 0x0713, 0x100B, 0x0C1F, 0xFFFF }, {
-      0x0320, 0x0335, 0x0A05, 0x0817, 0xFFFF }, { 0x0623, 0x0527, 0xFFFF }, { 0x0416, 0x061E, 0x0E0E, 0x0711, 0x0E09, 0x012F,
-      0xFFFF }, { 0x0322, 0x0504, 0x080A, 0x0919, 0x1D0F, 0xFFFF }, { 0x0631, 0x0903, 0x0815, 0x0C07, 0x042B, 0x032D, 0x0B1D,
-      0xFFFF }, { 0x022A, 0x0801, 0x0533, 0x0625, 0x0237, 0x0A1B, 0xFFFF }, { 0x0330, 0x0414, 0x0136, 0x0708, 0x0721, 0x0529,
-      0x140D, 0xFFFF }, { 0x0400, 0x0324, 0x0606, 0x0038, 0x0039, 0x110B, 0x0D1F, 0xFFFF }, { 0x051A, 0x0813, 0x0B05, 0x0917,
-      0xFFFF }, { 0x0723, 0x0435, 0x0627, 0xFFFF }, { 0x0412, 0x0234, 0x0F09, 0x022F, 0xFFFF }, { 0x0326, 0x0A0C, 0x012E,
-      0x0811, 0x0A19, 0x1E0F, 0xFFFF }, { 0x0731, 0x0A03, 0x0915, 0x0D07, 0x052B, 0xFFFF }, { 0x0410, 0x0901, 0x0633, 0x0725,
-      0x0337, 0x0B1B, 0x042D, 0x0C1D, 0xFFFF }, { 0x0518, 0x022C, 0x0629, 0x150D, 0xFFFF }, { 0x0332, 0x0502, 0x0821, 0x0139,
-      0x120B, 0x0E1F, 0xFFFF }, { 0x0328, 0x061C, 0x0913, 0x0A17, 0xFFFF }, { 0x0420, 0x0535, 0x0C05, 0x0727, 0xFFFF }, {
-      0x0823, 0x032F, 0xFFFF }, { 0x0516, 0x071E, 0x0F0E, 0x0911, 0x0B19, 0x1009, 0x1F0F, 0xFFFF }, { 0x0422, 0x0604, 0x090A,
-      0x0B03, 0x0A15, 0x0E07, 0x062B, 0xFFFF }, { 0x0831, 0x0A01, 0x0733, 0x052D, 0x0D1D, 0xFFFF }, { 0x032A, 0x0825, 0x0437,
-      0x0729, 0x0C1B, 0x160D, 0xFFFF }, { 0x0430, 0x0514, 0x0236, 0x0808, 0x0921, 0x0239, 0x130B, 0x0F1F, 0xFFFF }, { 0x0500,
-      0x0424, 0x0706, 0x0138, 0x0A13, 0x0B17, 0xFFFF }, { 0x061A, 0x0635, 0x0D05, 0xFFFF }, { 0x0923, 0x0827, 0xFFFF }, {
-      0x0512, 0x0334, 0x003A, 0x0A11, 0x1109, 0x003B, 0x042F, 0xFFFF }, { 0x0426, 0x0B0C, 0x022E, 0x0B15, 0x0F07, 0x0C19,
-      0x072B, 0xFFFF }, { 0x0931, 0x0B01, 0x0C03, 0x062D, 0x0E1D, 0xFFFF }, { 0x0510, 0x0833, 0x0925, 0x0537, 0x0D1B, 0x170D,
-      0xFFFF }, { 0x0618, 0x032C, 0x0A21, 0x0339, 0x0829, 0xFFFF }, { 0x0432, 0x0602, 0x0B13, 0x140B, 0x101F, 0xFFFF }, {
-      0x0428, 0x071C, 0x0735, 0x0E05, 0x0C17, 0xFFFF }, { 0x0520, 0x0A23, 0x0927, 0xFFFF }, { 0x0B11, 0x1209, 0x013B, 0x052F,
-      0xFFFF }, { 0x0616, 0x081E, 0x0D19, 0xFFFF }, { 0x0522, 0x0704, 0x0A0A, 0x0A31, 0x0D03, 0x0C15, 0x1007, 0x082B, 0x072D,
-      0x0F1D, 0xFFFF }, { 0x0C01, 0x0933, 0x0A25, 0x0637, 0x0E1B, 0xFFFF }, { 0x042A, 0x0B21, 0x0929, 0x180D, 0xFFFF }, {
-      0x0530, 0x0614, 0x0336, 0x0908, 0x0439, 0x150B, 0x111F, 0xFFFF }, { 0x0600, 0x0524, 0x0806, 0x0238, 0x0C13, 0x0F05,
-      0x0D17, 0xFFFF }, { 0x071A, 0x0B23, 0x0835, 0x0A27, 0xFFFF }, { 0x1309, 0x023B, 0x062F, 0xFFFF }, { 0x0612, 0x0434,
-      0x013A, 0x0C11, 0x0E19, 0xFFFF }, { 0x0526, 0x0C0C, 0x032E, 0x0B31, 0x0E03, 0x0D15, 0x1107, 0x092B, 0xFFFF }, { 0x0D01,
-      0x0A33, 0x0B25, 0x0737, 0x0F1B, 0x082D, 0x101D, 0xFFFF }, { 0x0610, 0x0A29, 0x190D, 0xFFFF }, { 0x0718, 0x042C, 0x0C21,
-      0x0539, 0x160B, 0x121F, 0xFFFF }, { 0x0532, 0x0702, 0x0D13, 0x0E17, 0xFFFF }, { 0x0528, 0x081C, 0x0935, 0x1005, 0x0B27,
-      0xFFFF }, { 0x0620, 0x0C23, 0x033B, 0x072F, 0xFFFF }, { 0x0D11, 0x0F19, 0x1409, 0xFFFF }, { 0x0716, 0x003C, 0x091E,
-      0x0F03, 0x0E15, 0x1207, 0x0A2B, 0x003D, 0xFFFF }, { 0x0622, 0x0804, 0x0B0A, 0x0C31, 0x0E01, 0x0B33, 0x092D, 0x111D,
-      0xFFFF }, { 0x0C25, 0x0837, 0x0B29, 0x101B, 0x1A0D, 0xFFFF }, { 0x052A, 0x0D21, 0x0639, 0x170B, 0x131F, 0xFFFF }, {
-      0x0630, 0x0714, 0x0436, 0x0A08, 0x0E13, 0x0F17, 0xFFFF }, { 0x0700, 0x0624, 0x0906, 0x0338, 0x0A35, 0x1105, 0xFFFF }, {
-      0x081A, 0x0D23, 0x0C27, 0xFFFF }, { 0x0E11, 0x1509, 0x043B, 0x082F, 0xFFFF }, { 0x0712, 0x0534, 0x023A, 0x0F15, 0x1307,
-      0x1019, 0x0B2B, 0x013D, 0xFFFF }, { 0x0626, 0x0D0C, 0x042E, 0x0D31, 0x0F01, 0x1003, 0x0A2D, 0x121D, 0xFFFF }, { 0x0C33,
-      0x0D25, 0x0937, 0x111B, 0x1B0D, 0xFFFF }, { 0x0710, 0x0E21, 0x0739, 0x0C29, 0xFFFF }, { 0x0818, 0x052C, 0x0F13, 0x180B,
-      0x141F, 0xFFFF }, { 0x0632, 0x0802, 0x0B35, 0x1205, 0x1017, 0xFFFF }, { 0x0628, 0x091C, 0x0E23, 0x0D27, 0xFFFF }, {
-      0x0720, 0x0F11, 0x1609, 0x053B, 0x092F, 0xFFFF }, { 0x1119, 0x023D, 0xFFFF }, { 0x0816, 0x013C, 0x0A1E, 0x0E31, 0x1103,
-      0x1015, 0x1407, 0x0C2B, 0x0B2D, 0x131D, 0xFFFF }, { 0x0722, 0x0904, 0x0C0A, 0x1001, 0x0D33, 0x0E25, 0x0A37, 0x121B,
-      0xFFFF }, { 0x0F21, 0x0D29, 0x1C0D, 0xFFFF }, { 0x062A, 0x0839, 0x190B, 0x151F, 0xFFFF }, { 0x0730, 0x0814, 0x0536,
-      0x0B08, 0x1013, 0x1305, 0x1117, 0xFFFF }, { 0x0800, 0x0724, 0x0A06, 0x0438, 0x0F23, 0x0C35, 0x0E27, 0xFFFF }, { 0x091A,
-      0x1709, 0x063B, 0x0A2F, 0xFFFF }, { 0x1011, 0x1219, 0x033D, 0xFFFF }, { 0x0812, 0x0634, 0x033A, 0x0F31, 0x1203, 0x1115,
-      0x1507, 0x0D2B, 0xFFFF }, { 0x0726, 0x0E0C, 0x052E, 0x1101, 0x0E33, 0x0F25, 0x0B37, 0x131B, 0x0C2D, 0x141D, 0xFFFF }, {
-      0x0E29, 0x1D0D, 0xFFFF }, { 0x0810, 0x1021, 0x0939, 0x1A0B, 0x161F, 0xFFFF }, { 0x0918, 0x062C, 0x1113, 0x1217, 0xFFFF
-      }, { 0x0732, 0x0902, 0x0D35, 0x1405, 0x0F27, 0xFFFF }, { 0x0728, 0x0A1C, 0x1023, 0x073B, 0x0B2F, 0xFFFF }, { 0x0820,
-      0x1111, 0x1319, 0x1809, 0xFFFF }, { 0x1303, 0x1215, 0x1607, 0x0E2B, 0x043D, 0xFFFF }, { 0x0916, 0x023C, 0x0B1E, 0x1031,
-      0x1201, 0x0F33, 0x0D2D, 0x151D, 0xFFFF }, { 0x0822, 0x0A04, 0x0D0A, 0x1025, 0x0C37, 0x0F29, 0x141B, 0x1E0D, 0xFFFF }, {
-      0x1121, 0x0A39, 0x1B0B, 0x171F, 0xFFFF }, { 0x072A, 0x1213, 0x1317, 0xFFFF }, { 0x0830, 0x0914, 0x0636, 0x0C08, 0x0E35,
-      0x1505, 0xFFFF }, { 0x0900, 0x0824, 0x0B06, 0x0538, 0x1123, 0x1027, 0xFFFF }, { 0x0A1A, 0x1211, 0x1909, 0x083B, 0x0C2F,
-      0xFFFF }, { 0x1315, 0x1707, 0x1419, 0x0F2B, 0x053D, 0xFFFF }, { 0x0912, 0x0734, 0x043A, 0x1131, 0x1301, 0x1403, 0x0E2D,
-      0x161D, 0xFFFF }, { 0x0826, 0x0F0C, 0x062E, 0x1033, 0x1125, 0x0D37, 0x151B, 0x1F0D, 0xFFFF }, { 0x1221, 0x0B39, 0x1029,
-      0xFFFF }, { 0x0910, 0x1313, 0x1C0B, 0x181F, 0xFFFF }, { 0x0A18, 0x072C, 0x0F35, 0x1605, 0x1417, 0xFFFF }, { 0x0832,
-      0x0A02, 0x1223, 0x1127, 0xFFFF }, { 0x0828, 0x0B1C, 0x1311, 0x1A09, 0x093B, 0x0D2F, 0xFFFF }, { 0x0920, 0x1519, 0x063D,
-      0xFFFF }, { 0x1231, 0x1503, 0x1415, 0x1807, 0x102B, 0x0F2D, 0x171D, 0xFFFF }, { 0x0A16, 0x033C, 0x0C1E, 0x1401, 0x1133,
-      0x1225, 0x0E37, 0x161B, 0xFFFF }, { 0x0922, 0x0B04, 0x0E0A, 0x1321, 0x1129, 0xFFFF }, { 0x0C39, 0x1D0B, 0x191F, 0xFFFF
-      }, { 0x082A, 0x1413, 0x1705, 0x1517, 0xFFFF }, { 0x0930, 0x0A14, 0x0736, 0x0D08, 0x1323, 0x1035, 0x1227, 0xFFFF }, {
-      0x0A00, 0x0924, 0x0C06, 0x0638, 0x1B09, 0x0A3B, 0x0E2F, 0xFFFF }, { 0x0B1A, 0x1411, 0x1619, 0x073D, 0xFFFF }, { 0x1331,
-      0x1603, 0x1515, 0x1907, 0x112B, 0xFFFF }, { 0x0A12, 0x0834, 0x053A, 0x1501, 0x1233, 0x1325, 0x0F37, 0x171B, 0x102D,
-      0x181D, 0xFFFF }, { 0x0926, 0x072E, 0x1229, 0xFFFF }, { 0x1421, 0x0D39, 0x1E0B, 0x1A1F, 0xFFFF }, { 0x0A10, 0x1513,
-      0x1617, 0xFFFF }, { 0x0B18, 0x082C, 0x1135, 0x1805, 0x1327, 0xFFFF }, { 0x0932, 0x0B02, 0x1423, 0x0B3B, 0x0F2F, 0xFFFF
-      }, { 0x0928, 0x0C1C, 0x1511, 0x1719, 0x1C09, 0xFFFF }, { 0x0A20, 0x1703, 0x1615, 0x1A07, 0x122B, 0x083D, 0xFFFF }, {
-      0x1431, 0x1601, 0x1333, 0x112D, 0x191D, 0xFFFF }, { 0x0B16, 0x043C, 0x0D1E, 0x1425, 0x1037, 0x1329, 0x181B, 0xFFFF }, {
-      0x0A22, 0x0C04, 0x0F0A, 0x1521, 0x0E39, 0x1F0B, 0x1B1F, 0xFFFF }, { 0x1613, 0x1717, 0xFFFF }, { 0x092A, 0x1235, 0x1905,
-      0xFFFF }, { 0x0A30, 0x0B14, 0x0836, 0x0E08, 0x1523, 0x1427, 0xFFFF }, { 0x0B00, 0x0A24, 0x0D06, 0x0738, 0x1611, 0x1D09,
-      0x0C3B, 0x102F, 0xFFFF }, { 0x0C1A, 0x1715, 0x1B07, 0x1819, 0x132B, 0x093D, 0xFFFF }, { 0x1531, 0x1701, 0x1803, 0x122D,
-      0x1A1D, 0xFFFF }, { 0x0B12, 0x0934, 0x063A, 0x1433, 0x1525, 0x1137, 0x191B, 0xFFFF }, { 0x0A26, 0x003E, 0x082E, 0x1621,
-      0x0F39, 0x1429, 0x003F, 0xFFFF }, { 0x1713, 0x1C1F, 0xFFFF }, { 0x0B10, 0x1335, 0x1A05, 0x1817, 0xFFFF }, { 0x0C18,
-      0x092C, 0x1623, 0x1527, 0xFFFF }, { 0x0A32, 0x0C02, 0x1711, 0x1E09, 0x0D3B, 0x112F, 0xFFFF }, { 0x0A28, 0x0D1C, 0x1919,
-      0x0A3D, 0xFFFF }, { 0x0B20, 0x1631, 0x1903, 0x1815, 0x1C07, 0x142B, 0x132D, 0x1B1D, 0xFFFF }, { 0x1801, 0x1533, 0x1625,
-      0x1237, 0x1A1B, 0xFFFF }, { 0x0C16, 0x053C, 0x0E1E, 0x1721, 0x1529, 0x013F, 0xFFFF }, { 0x0B22, 0x0D04, 0x1039, 0x1D1F,
-      0xFFFF }, { 0x1813, 0x1B05, 0x1917, 0xFFFF }, { 0x0A2A, 0x1723, 0x1435, 0x1627, 0xFFFF }, { 0x0B30, 0x0C14, 0x0936,
-      0x0F08, 0x1F09, 0x0E3B, 0x122F, 0xFFFF }, { 0x0C00, 0x0B24, 0x0E06, 0x0838, 0x1811, 0x1A19, 0x0B3D, 0xFFFF }, { 0x0D1A,
-      0x1731, 0x1A03, 0x1915, 0x1D07, 0x152B, 0xFFFF }, { 0x1901, 0x1633, 0x1725, 0x1337, 0x1B1B, 0x142D, 0x1C1D, 0xFFFF }, {
-      0x0C12, 0x0A34, 0x073A, 0x1629, 0x023F, 0xFFFF }, { 0x0B26, 0x013E, 0x092E, 0x1821, 0x1139, 0x1E1F, 0xFFFF }, { 0x1913,
-      0x1A17, 0xFFFF }, { 0x0C10, 0x1535, 0x1C05, 0x1727, 0xFFFF }, { 0x0D18, 0x0A2C, 0x1823, 0x0F3B, 0x132F, 0xFFFF }, {
-      0x0B32, 0x0D02, 0x1911, 0x1B19, 0xFFFF }, { 0x0B28, 0x0E1C, 0x1B03, 0x1A15, 0x1E07, 0x162B, 0x0C3D, 0xFFFF }, { 0x0C20,
-      0x1831, 0x1A01, 0x1733, 0x152D, 0x1D1D, 0xFFFF }, { 0x1825, 0x1437, 0x1729, 0x1C1B, 0x033F, 0xFFFF }, { 0x0D16, 0x063C,
-      0x0F1E, 0x1921, 0x1239, 0x1F1F, 0xFFFF }, { 0x0C22, 0x0E04, 0x1A13, 0x1B17, 0xFFFF }, { 0x1635, 0x1D05, 0xFFFF }, {
-      0x0B2A, 0x1923, 0x1827, 0xFFFF }, { 0x0C30, 0x0D14, 0x0A36, 0x1A11, 0x103B, 0x142F, 0xFFFF }, { 0x0D00, 0x0C24, 0x0F06,
-      0x0938, 0x1B15, 0x1F07, 0x1C19, 0x172B, 0x0D3D, 0xFFFF }, { 0x0E1A, 0x1931, 0x1B01, 0x1C03, 0x162D, 0x1E1D, 0xFFFF }, {
-      0x1833, 0x1925, 0x1537, 0x1D1B, 0xFFFF }, { 0x0D12, 0x0B34, 0x083A, 0x1A21, 0x1339, 0x1829, 0x043F, 0xFFFF }, { 0x0C26,
-      0x023E, 0x0A2E, 0x1B13, 0xFFFF }, { 0x1735, 0x1E05, 0x1C17, 0xFFFF }, { 0x0D10, 0x1A23, 0x1927, 0xFFFF }, { 0x0E18,
-      0x0B2C, 0x1B11, 0x113B, 0x152F, 0xFFFF }, { 0x0C32, 0x0E02, 0x1D19, 0x0E3D, 0xFFFF }, { 0x0C28, 0x0F1C, 0x1A31, 0x1D03,
-      0x1C15, 0x182B, 0x172D, 0x1F1D, 0xFFFF }, { 0x0D20, 0x1C01, 0x1933, 0x1A25, 0x1637, 0x1E1B, 0xFFFF }, { 0x1B21, 0x1929,
-      0x053F, 0xFFFF }, { 0x0E16, 0x073C, 0x1439, 0xFFFF }, { 0x0D22, 0x0F04, 0x1C13, 0x1F05, 0x1D17, 0xFFFF }, { 0x1B23,
-      0x1835, 0x1A27, 0xFFFF }, { 0x0C2A, 0x123B, 0x162F, 0xFFFF }, { 0x0D30, 0x0E14, 0x0B36, 0x1C11, 0x1E19, 0x0F3D, 0xFFFF
-      }, { 0x0E00, 0x0D24, 0x0A38, 0x1B31, 0x1E03, 0x1D15, 0x192B, 0xFFFF }, { 0x0F1A, 0x1D01, 0x1A33, 0x1B25, 0x1737, 0x1F1B,
-      0x182D, 0xFFFF }, { 0x1A29, 0x063F, 0xFFFF }, { 0x0E12, 0x0C34, 0x093A, 0x1C21, 0x1539, 0xFFFF }, { 0x0D26, 0x033E,
-      0x0B2E, 0x1D13, 0x1E17, 0xFFFF }, { 0x1935, 0x1B27, 0xFFFF }, { 0x0E10, 0x1C23, 0x133B, 0x172F, 0xFFFF }, { 0x0F18,
-      0x0C2C, 0x1D11, 0x1F19, 0xFFFF }, { 0x0D32, 0x0F02, 0x1F03, 0x1E15, 0x1A2B, 0x103D, 0xFFFF }, { 0x0D28, 0x1C31, 0x1E01,
-      0x1B33, 0x192D, 0xFFFF }, { 0x0E20, 0x1C25, 0x1837, 0x1B29, 0x073F, 0xFFFF }, { 0x1D21, 0x1639, 0xFFFF }, { 0x0F16,
-      0x083C, 0x1E13, 0x1F17, 0xFFFF }, { 0x0E22, 0x1A35, 0xFFFF }, { 0x1D23, 0x1C27, 0xFFFF }, { 0x0D2A, 0x1E11, 0x143B,
-      0x182F, 0xFFFF }, { 0x0E30, 0x0F14, 0x0C36, 0x1F15, 0x1B2B, 0x113D, 0xFFFF }, { 0x0F00, 0x0E24, 0x0B38, 0x1D31, 0x1F01,
-      0x1A2D, 0xFFFF }, { 0x1C33, 0x1D25, 0x1937, 0xFFFF }, { 0x1E21, 0x1739, 0x1C29, 0x083F, 0xFFFF }, { 0x0F12, 0x0D34,
-      0x0A3A, 0x1F13, 0xFFFF }, { 0x0E26, 0x043E, 0x0C2E, 0x1B35, 0xFFFF }, { 0x1E23, 0x1D27, 0xFFFF }, { 0x0F10, 0x1F11,
-      0x153B, 0x192F, 0xFFFF }, { 0x0D2C, 0x123D, 0xFFFF },
-   };
-
-   struct etc1_block
-   {
-      // big endian uint64:
-      // bit ofs:  56  48  40  32  24  16   8   0
-      // byte ofs: b0, b1, b2, b3, b4, b5, b6, b7 
-      union 
-      {
-         uint64 m_uint64;
-         uint8 m_bytes[8];
-      };
-
-      uint8 m_low_color[2];
-      uint8 m_high_color[2];
-
-      enum { cNumSelectorBytes = 4 };
-      uint8 m_selectors[cNumSelectorBytes];
-
-      inline void clear()
-      {
-         zero_this(this);
-      }
-
-      inline uint get_byte_bits(uint ofs, uint num) const
-      {
-         RG_ETC1_ASSERT((ofs + num) <= 64U);
-         RG_ETC1_ASSERT(num && (num <= 8U));
-         RG_ETC1_ASSERT((ofs >> 3) == ((ofs + num - 1) >> 3));
-         const uint byte_ofs = 7 - (ofs >> 3);
-         const uint byte_bit_ofs = ofs & 7;
-         return (m_bytes[byte_ofs] >> byte_bit_ofs) & ((1 << num) - 1);
-      }
-
-      inline void set_byte_bits(uint ofs, uint num, uint bits)
-      {
-         RG_ETC1_ASSERT((ofs + num) <= 64U);
-         RG_ETC1_ASSERT(num && (num < 32U));
-         RG_ETC1_ASSERT((ofs >> 3) == ((ofs + num - 1) >> 3));
-         RG_ETC1_ASSERT(bits < (1U << num));
-         const uint byte_ofs = 7 - (ofs >> 3);
-         const uint byte_bit_ofs = ofs & 7;
-         const uint mask = (1 << num) - 1;
-         m_bytes[byte_ofs] &= ~(mask << byte_bit_ofs);
-         m_bytes[byte_ofs] |= (bits << byte_bit_ofs);
-      }
-
-      // false = left/right subblocks
-      // true = upper/lower subblocks
-      inline bool get_flip_bit() const 
-      {
-         return (m_bytes[3] & 1) != 0;
-      }   
-
-      inline void set_flip_bit(bool flip)
-      {
-         m_bytes[3] &= ~1;
-         m_bytes[3] |= static_cast<uint8>(flip);
-      }
-
-      inline bool get_diff_bit() const
-      {
-         return (m_bytes[3] & 2) != 0;
-      }
-
-      inline void set_diff_bit(bool diff)
-      {
-         m_bytes[3] &= ~2;
-         m_bytes[3] |= (static_cast<uint>(diff) << 1);
-      }
-
-      // Returns intensity modifier table (0-7) used by subblock subblock_id.
-      // subblock_id=0 left/top (CW 1), 1=right/bottom (CW 2)
-      inline uint get_inten_table(uint subblock_id) const
-      {
-         RG_ETC1_ASSERT(subblock_id < 2);
-         const uint ofs = subblock_id ? 2 : 5;
-         return (m_bytes[3] >> ofs) & 7;
-      }
-
-      // Sets intensity modifier table (0-7) used by subblock subblock_id (0 or 1)
-      inline void set_inten_table(uint subblock_id, uint t)
-      {
-         RG_ETC1_ASSERT(subblock_id < 2);
-         RG_ETC1_ASSERT(t < 8);
-         const uint ofs = subblock_id ? 2 : 5;
-         m_bytes[3] &= ~(7 << ofs);
-         m_bytes[3] |= (t << ofs);
-      }
-
-      // Returned selector value ranges from 0-3 and is a direct index into g_etc1_inten_tables.
-      inline uint get_selector(uint x, uint y) const
-      {
-         RG_ETC1_ASSERT((x | y) < 4);
-
-         const uint bit_index = x * 4 + y;
-         const uint byte_bit_ofs = bit_index & 7;
-         const uint8 *p = &m_bytes[7 - (bit_index >> 3)];
-         const uint lsb = (p[0] >> byte_bit_ofs) & 1;
-         const uint msb = (p[-2] >> byte_bit_ofs) & 1;
-         const uint val = lsb | (msb << 1);
-
-         return g_etc1_to_selector_index[val];
-      }
-
-      // Selector "val" ranges from 0-3 and is a direct index into g_etc1_inten_tables.
-      inline void set_selector(uint x, uint y, uint val)
-      {
-         RG_ETC1_ASSERT((x | y | val) < 4);
-         const uint bit_index = x * 4 + y;
-
-         uint8 *p = &m_bytes[7 - (bit_index >> 3)];
-
-         const uint byte_bit_ofs = bit_index & 7;
-         const uint mask = 1 << byte_bit_ofs;
-
-         const uint etc1_val = g_selector_index_to_etc1[val];
-
-         const uint lsb = etc1_val & 1;
-         const uint msb = etc1_val >> 1;
-
-         p[0] &= ~mask;
-         p[0] |= (lsb << byte_bit_ofs);
-
-         p[-2] &= ~mask;
-         p[-2] |= (msb << byte_bit_ofs);
-      }
-
-      inline void set_base4_color(uint idx, uint16 c)
-      {
-         if (idx)
-         {
-            set_byte_bits(cETC1AbsColor4R2BitOffset, 4, (c >> 8) & 15);
-            set_byte_bits(cETC1AbsColor4G2BitOffset, 4, (c >> 4) & 15);
-            set_byte_bits(cETC1AbsColor4B2BitOffset, 4, c & 15);
-         }
-         else
-         {
-            set_byte_bits(cETC1AbsColor4R1BitOffset, 4, (c >> 8) & 15);
-            set_byte_bits(cETC1AbsColor4G1BitOffset, 4, (c >> 4) & 15);
-            set_byte_bits(cETC1AbsColor4B1BitOffset, 4, c & 15);
-         }
-      }
-
-      inline uint16 get_base4_color(uint idx) const
-      {
-         uint r, g, b;
-         if (idx)
-         {
-            r = get_byte_bits(cETC1AbsColor4R2BitOffset, 4);
-            g = get_byte_bits(cETC1AbsColor4G2BitOffset, 4);
-            b = get_byte_bits(cETC1AbsColor4B2BitOffset, 4);
-         }
-         else
-         {
-            r = get_byte_bits(cETC1AbsColor4R1BitOffset, 4);
-            g = get_byte_bits(cETC1AbsColor4G1BitOffset, 4);
-            b = get_byte_bits(cETC1AbsColor4B1BitOffset, 4);
-         }
-         return static_cast<uint16>(b | (g << 4U) | (r << 8U));
-      }
-
-      inline void set_base5_color(uint16 c)
-      {
-         set_byte_bits(cETC1BaseColor5RBitOffset, 5, (c >> 10) & 31);
-         set_byte_bits(cETC1BaseColor5GBitOffset, 5, (c >> 5) & 31);
-         set_byte_bits(cETC1BaseColor5BBitOffset, 5, c & 31);
-      }
-
-      inline uint16 get_base5_color() const
-      {
-         const uint r = get_byte_bits(cETC1BaseColor5RBitOffset, 5);
-         const uint g = get_byte_bits(cETC1BaseColor5GBitOffset, 5);
-         const uint b = get_byte_bits(cETC1BaseColor5BBitOffset, 5);
-         return static_cast<uint16>(b | (g << 5U) | (r << 10U));
-      }
-
-      void set_delta3_color(uint16 c)
-      {
-         set_byte_bits(cETC1DeltaColor3RBitOffset, 3, (c >> 6) & 7);
-         set_byte_bits(cETC1DeltaColor3GBitOffset, 3, (c >> 3) & 7);
-         set_byte_bits(cETC1DeltaColor3BBitOffset, 3, c & 7);
-      }
-
-      inline uint16 get_delta3_color() const
-      {
-         const uint r = get_byte_bits(cETC1DeltaColor3RBitOffset, 3);
-         const uint g = get_byte_bits(cETC1DeltaColor3GBitOffset, 3);
-         const uint b = get_byte_bits(cETC1DeltaColor3BBitOffset, 3);
-         return static_cast<uint16>(b | (g << 3U) | (r << 6U));
-      }
-
-      // Base color 5
-      static uint16 pack_color5(const color_quad_u8& color, bool scaled, uint bias = 127U);
-      static uint16 pack_color5(uint r, uint g, uint b, bool scaled, uint bias = 127U);
-
-      static color_quad_u8 unpack_color5(uint16 packed_color5, bool scaled, uint alpha = 255U);
-      static void unpack_color5(uint& r, uint& g, uint& b, uint16 packed_color, bool scaled);
-
-      static bool unpack_color5(color_quad_u8& result, uint16 packed_color5, uint16 packed_delta3, bool scaled, uint alpha = 255U);
-      static bool unpack_color5(uint& r, uint& g, uint& b, uint16 packed_color5, uint16 packed_delta3, bool scaled, uint alpha = 255U);
-
-      // Delta color 3
-      // Inputs range from -4 to 3 (cETC1ColorDeltaMin to cETC1ColorDeltaMax)
-      static uint16 pack_delta3(int r, int g, int b);
-
-      // Results range from -4 to 3 (cETC1ColorDeltaMin to cETC1ColorDeltaMax)
-      static void unpack_delta3(int& r, int& g, int& b, uint16 packed_delta3);
-
-      // Abs color 4
-      static uint16 pack_color4(const color_quad_u8& color, bool scaled, uint bias = 127U);
-      static uint16 pack_color4(uint r, uint g, uint b, bool scaled, uint bias = 127U);
-
-      static color_quad_u8 unpack_color4(uint16 packed_color4, bool scaled, uint alpha = 255U);
-      static void unpack_color4(uint& r, uint& g, uint& b, uint16 packed_color4, bool scaled);
-
-      // subblock colors
-      static void get_diff_subblock_colors(color_quad_u8* pDst, uint16 packed_color5, uint table_idx);
-      static bool get_diff_subblock_colors(color_quad_u8* pDst, uint16 packed_color5, uint16 packed_delta3, uint table_idx);
-      static void get_abs_subblock_colors(color_quad_u8* pDst, uint16 packed_color4, uint table_idx);
-
-      static inline void unscaled_to_scaled_color(color_quad_u8& dst, const color_quad_u8& src, bool color4)
-      {
-         if (color4)
-         {
-            dst.r = src.r | (src.r << 4);
-            dst.g = src.g | (src.g << 4);
-            dst.b = src.b | (src.b << 4);
-         }
-         else
-         {
-            dst.r = (src.r >> 2) | (src.r << 3);
-            dst.g = (src.g >> 2) | (src.g << 3);
-            dst.b = (src.b >> 2) | (src.b << 3);
-         }
-         dst.a = src.a;
-      }
-   };
-
-   // Returns pointer to sorted array.
-   template<typename T, typename Q>
-   T* indirect_radix_sort(uint num_indices, T* pIndices0, T* pIndices1, const Q* pKeys, uint key_ofs, uint key_size, bool init_indices)
-   {  
-      RG_ETC1_ASSERT((key_ofs >= 0) && (key_ofs < sizeof(T)));
-      RG_ETC1_ASSERT((key_size >= 1) && (key_size <= 4));
-
-      if (init_indices)
-      {
-         T* p = pIndices0;
-         T* q = pIndices0 + (num_indices >> 1) * 2;
-         uint i;
-         for (i = 0; p != q; p += 2, i += 2)
-         {
-            p[0] = static_cast<T>(i);
-            p[1] = static_cast<T>(i + 1); 
-         }
-
-         if (num_indices & 1)
-            *p = static_cast<T>(i);
-      }
-
-      uint hist[256 * 4];
-
-      memset(hist, 0, sizeof(hist[0]) * 256 * key_size);
-
-#define RG_ETC1_GET_KEY(p) (*(const uint*)((const uint8*)(pKeys + *(p)) + key_ofs))
-#define RG_ETC1_GET_KEY_FROM_INDEX(i) (*(const uint*)((const uint8*)(pKeys + (i)) + key_ofs))
-
-      if (key_size == 4)
-      {
-         T* p = pIndices0;
-         T* q = pIndices0 + num_indices;
-         for ( ; p != q; p++)
-         {
-            const uint key = RG_ETC1_GET_KEY(p);
-
-            hist[        key        & 0xFF]++;
-            hist[256 + ((key >>  8) & 0xFF)]++;
-            hist[512 + ((key >> 16) & 0xFF)]++;
-            hist[768 + ((key >> 24) & 0xFF)]++;
-         }
-      }
-      else if (key_size == 3)
-      {
-         T* p = pIndices0;
-         T* q = pIndices0 + num_indices;
-         for ( ; p != q; p++)
-         {
-            const uint key = RG_ETC1_GET_KEY(p);
-
-            hist[        key        & 0xFF]++;
-            hist[256 + ((key >>  8) & 0xFF)]++;
-            hist[512 + ((key >> 16) & 0xFF)]++;
-         }
-      }   
-      else if (key_size == 2)
-      {
-         T* p = pIndices0;
-         T* q = pIndices0 + (num_indices >> 1) * 2;
-
-         for ( ; p != q; p += 2)
-         {
-            const uint key0 = RG_ETC1_GET_KEY(p);
-            const uint key1 = RG_ETC1_GET_KEY(p+1);
-
-            hist[        key0         & 0xFF]++;
-            hist[256 + ((key0 >>  8) & 0xFF)]++;
-
-            hist[        key1        & 0xFF]++;
-            hist[256 + ((key1 >>  8) & 0xFF)]++;
-         }
-
-         if (num_indices & 1)
-         {
-            const uint key = RG_ETC1_GET_KEY(p);
-
-            hist[        key        & 0xFF]++;
-            hist[256 + ((key >>  8) & 0xFF)]++;
-         }
-      }      
-      else
-      {
-         RG_ETC1_ASSERT(key_size == 1);
-         if (key_size != 1)
-            return NULL;
-
-         T* p = pIndices0;
-         T* q = pIndices0 + (num_indices >> 1) * 2;
-
-         for ( ; p != q; p += 2)
-         {
-            const uint key0 = RG_ETC1_GET_KEY(p);
-            const uint key1 = RG_ETC1_GET_KEY(p+1);
-
-            hist[key0 & 0xFF]++;
-            hist[key1 & 0xFF]++;
-         }
-
-         if (num_indices & 1)
-         {
-            const uint key = RG_ETC1_GET_KEY(p);
-
-            hist[key & 0xFF]++;
-         }
-      }      
-
-      T* pCur = pIndices0;
-      T* pNew = pIndices1;
-
-      for (uint pass = 0; pass < key_size; pass++)
-      {
-         const uint* pHist = &hist[pass << 8];
-
-         uint offsets[256];
-
-         uint cur_ofs = 0;
-         for (uint i = 0; i < 256; i += 2)
-         {
-            offsets[i] = cur_ofs;
-            cur_ofs += pHist[i];
-
-            offsets[i+1] = cur_ofs;
-            cur_ofs += pHist[i+1];
-         }
-
-         const uint pass_shift = pass << 3;
-
-         T* p = pCur;
-         T* q = pCur + (num_indices >> 1) * 2;
-
-         for ( ; p != q; p += 2)
-         {
-            uint index0 = p[0];
-            uint index1 = p[1];
-
-            uint c0 = (RG_ETC1_GET_KEY_FROM_INDEX(index0) >> pass_shift) & 0xFF;
-            uint c1 = (RG_ETC1_GET_KEY_FROM_INDEX(index1) >> pass_shift) & 0xFF;
-
-            if (c0 == c1)
-            {
-               uint dst_offset0 = offsets[c0];
-
-               offsets[c0] = dst_offset0 + 2;
-
-               pNew[dst_offset0] = static_cast<T>(index0);
-               pNew[dst_offset0 + 1] = static_cast<T>(index1);
-            }
-            else
-            {
-               uint dst_offset0 = offsets[c0]++;
-               uint dst_offset1 = offsets[c1]++;
-
-               pNew[dst_offset0] = static_cast<T>(index0);
-               pNew[dst_offset1] = static_cast<T>(index1);
-            }
-         }
-
-         if (num_indices & 1)
-         {
-            uint index = *p;
-            uint c = (RG_ETC1_GET_KEY_FROM_INDEX(index) >> pass_shift) & 0xFF;
-
-            uint dst_offset = offsets[c];
-            offsets[c] = dst_offset + 1;
-
-            pNew[dst_offset] = static_cast<T>(index);
-         }
-
-         T* t = pCur;
-         pCur = pNew;
-         pNew = t;
-      }            
-
-      return pCur;
-   }
-
-#undef RG_ETC1_GET_KEY
-#undef RG_ETC1_GET_KEY_FROM_INDEX
-
-   uint16 etc1_block::pack_color5(const color_quad_u8& color, bool scaled, uint bias)
-   {
-      return pack_color5(color.r, color.g, color.b, scaled, bias);
-   }
-   
-   uint16 etc1_block::pack_color5(uint r, uint g, uint b, bool scaled, uint bias)
-   {
-      if (scaled)
-      {
-         r = (r * 31U + bias) / 255U;
-         g = (g * 31U + bias) / 255U;
-         b = (b * 31U + bias) / 255U;
-      }
-
-      r = rg_etc1::minimum(r, 31U);
-      g = rg_etc1::minimum(g, 31U);
-      b = rg_etc1::minimum(b, 31U);
-
-      return static_cast<uint16>(b | (g << 5U) | (r << 10U));
-   }
-
-   color_quad_u8 etc1_block::unpack_color5(uint16 packed_color5, bool scaled, uint alpha)
-   {
-      uint b = packed_color5 & 31U;
-      uint g = (packed_color5 >> 5U) & 31U;
-      uint r = (packed_color5 >> 10U) & 31U;
-
-      if (scaled)
-      {
-         b = (b << 3U) | (b >> 2U);
-         g = (g << 3U) | (g >> 2U);
-         r = (r << 3U) | (r >> 2U);
-      }
-
-      return color_quad_u8(cNoClamp, r, g, b, rg_etc1::minimum(alpha, 255U));
-   }
-
-   void etc1_block::unpack_color5(uint& r, uint& g, uint& b, uint16 packed_color5, bool scaled)
-   {
-      color_quad_u8 c(unpack_color5(packed_color5, scaled, 0));
-      r = c.r;
-      g = c.g;
-      b = c.b;
-   }
-
-   bool etc1_block::unpack_color5(color_quad_u8& result, uint16 packed_color5, uint16 packed_delta3, bool scaled, uint alpha)
-   {
-      int dc_r, dc_g, dc_b;
-      unpack_delta3(dc_r, dc_g, dc_b, packed_delta3);
-      
-      int b = (packed_color5 & 31U) + dc_b;
-      int g = ((packed_color5 >> 5U) & 31U) + dc_g;
-      int r = ((packed_color5 >> 10U) & 31U) + dc_r;
-
-      bool success = true;
-      if (static_cast<uint>(r | g | b) > 31U)
-      {
-         success = false;
-         r = rg_etc1::clamp<int>(r, 0, 31);
-         g = rg_etc1::clamp<int>(g, 0, 31);
-         b = rg_etc1::clamp<int>(b, 0, 31);
-      }
-
-      if (scaled)
-      {
-         b = (b << 3U) | (b >> 2U);
-         g = (g << 3U) | (g >> 2U);
-         r = (r << 3U) | (r >> 2U);
-      }
-
-      result.set_noclamp_rgba(r, g, b, rg_etc1::minimum(alpha, 255U));
-      return success;
-   }
-
-   bool etc1_block::unpack_color5(uint& r, uint& g, uint& b, uint16 packed_color5, uint16 packed_delta3, bool scaled, uint alpha)
-   {
-      color_quad_u8 result;
-      const bool success = unpack_color5(result, packed_color5, packed_delta3, scaled, alpha);
-      r = result.r;
-      g = result.g;
-      b = result.b;
-      return success;
-   }
-     
-   uint16 etc1_block::pack_delta3(int r, int g, int b)
-   {
-      RG_ETC1_ASSERT((r >= cETC1ColorDeltaMin) && (r <= cETC1ColorDeltaMax));
-      RG_ETC1_ASSERT((g >= cETC1ColorDeltaMin) && (g <= cETC1ColorDeltaMax));
-      RG_ETC1_ASSERT((b >= cETC1ColorDeltaMin) && (b <= cETC1ColorDeltaMax));
-      if (r < 0) r += 8;
-      if (g < 0) g += 8;
-      if (b < 0) b += 8;
-      return static_cast<uint16>(b | (g << 3) | (r << 6));
-   }
-   
-   void etc1_block::unpack_delta3(int& r, int& g, int& b, uint16 packed_delta3)
-   {
-      r = (packed_delta3 >> 6) & 7;
-      g = (packed_delta3 >> 3) & 7;
-      b = packed_delta3 & 7;
-      if (r >= 4) r -= 8;
-      if (g >= 4) g -= 8;
-      if (b >= 4) b -= 8;
-   }
-
-   uint16 etc1_block::pack_color4(const color_quad_u8& color, bool scaled, uint bias)
-   {
-      return pack_color4(color.r, color.g, color.b, scaled, bias);
-   }
-   
-   uint16 etc1_block::pack_color4(uint r, uint g, uint b, bool scaled, uint bias)
-   {
-      if (scaled)
-      {
-         r = (r * 15U + bias) / 255U;
-         g = (g * 15U + bias) / 255U;
-         b = (b * 15U + bias) / 255U;
-      }
-
-      r = rg_etc1::minimum(r, 15U);
-      g = rg_etc1::minimum(g, 15U);
-      b = rg_etc1::minimum(b, 15U);
-
-      return static_cast<uint16>(b | (g << 4U) | (r << 8U));
-   }
-
-   color_quad_u8 etc1_block::unpack_color4(uint16 packed_color4, bool scaled, uint alpha)
-   {
-      uint b = packed_color4 & 15U;
-      uint g = (packed_color4 >> 4U) & 15U;
-      uint r = (packed_color4 >> 8U) & 15U;
-
-      if (scaled)
-      {
-         b = (b << 4U) | b;
-         g = (g << 4U) | g;
-         r = (r << 4U) | r;
-      }
-
-      return color_quad_u8(cNoClamp, r, g, b, rg_etc1::minimum(alpha, 255U));
-   }
-   
-   void etc1_block::unpack_color4(uint& r, uint& g, uint& b, uint16 packed_color4, bool scaled)
-   {
-      color_quad_u8 c(unpack_color4(packed_color4, scaled, 0));
-      r = c.r;
-      g = c.g;
-      b = c.b;
-   }
-
-   void etc1_block::get_diff_subblock_colors(color_quad_u8* pDst, uint16 packed_color5, uint table_idx)
-   {
-      RG_ETC1_ASSERT(table_idx < cETC1IntenModifierValues);
-      const int *pInten_modifer_table = &g_etc1_inten_tables[table_idx][0];
-
-      uint r, g, b;
-      unpack_color5(r, g, b, packed_color5, true);
-
-      const int ir = static_cast<int>(r), ig = static_cast<int>(g), ib = static_cast<int>(b);
-
-      const int y0 = pInten_modifer_table[0];
-      pDst[0].set(ir + y0, ig + y0, ib + y0);
-
-      const int y1 = pInten_modifer_table[1];
-      pDst[1].set(ir + y1, ig + y1, ib + y1);
-
-      const int y2 = pInten_modifer_table[2];
-      pDst[2].set(ir + y2, ig + y2, ib + y2);
-
-      const int y3 = pInten_modifer_table[3];
-      pDst[3].set(ir + y3, ig + y3, ib + y3);
-   }
-   
-   bool etc1_block::get_diff_subblock_colors(color_quad_u8* pDst, uint16 packed_color5, uint16 packed_delta3, uint table_idx)
-   {
-      RG_ETC1_ASSERT(table_idx < cETC1IntenModifierValues);
-      const int *pInten_modifer_table = &g_etc1_inten_tables[table_idx][0];
-
-      uint r, g, b;
-      bool success = unpack_color5(r, g, b, packed_color5, packed_delta3, true);
-
-      const int ir = static_cast<int>(r), ig = static_cast<int>(g), ib = static_cast<int>(b);
-
-      const int y0 = pInten_modifer_table[0];
-      pDst[0].set(ir + y0, ig + y0, ib + y0);
-
-      const int y1 = pInten_modifer_table[1];
-      pDst[1].set(ir + y1, ig + y1, ib + y1);
-
-      const int y2 = pInten_modifer_table[2];
-      pDst[2].set(ir + y2, ig + y2, ib + y2);
-
-      const int y3 = pInten_modifer_table[3];
-      pDst[3].set(ir + y3, ig + y3, ib + y3);
-
-      return success;
-   }
-   
-   void etc1_block::get_abs_subblock_colors(color_quad_u8* pDst, uint16 packed_color4, uint table_idx)
-   {
-      RG_ETC1_ASSERT(table_idx < cETC1IntenModifierValues);
-      const int *pInten_modifer_table = &g_etc1_inten_tables[table_idx][0];
-
-      uint r, g, b;
-      unpack_color4(r, g, b, packed_color4, true);
-      
-      const int ir = static_cast<int>(r), ig = static_cast<int>(g), ib = static_cast<int>(b);
-
-      const int y0 = pInten_modifer_table[0];
-      pDst[0].set(ir + y0, ig + y0, ib + y0);
-      
-      const int y1 = pInten_modifer_table[1];
-      pDst[1].set(ir + y1, ig + y1, ib + y1);
-
-      const int y2 = pInten_modifer_table[2];
-      pDst[2].set(ir + y2, ig + y2, ib + y2);
-
-      const int y3 = pInten_modifer_table[3];
-      pDst[3].set(ir + y3, ig + y3, ib + y3);
-   }
-      
-   bool unpack_etc1_block(const void* pETC1_block, unsigned int* pDst_pixels_rgba, bool preserve_alpha)
-   {
-      color_quad_u8* pDst = reinterpret_cast<color_quad_u8*>(pDst_pixels_rgba);
-      const etc1_block& block = *static_cast<const etc1_block*>(pETC1_block);
-
-      const bool diff_flag = block.get_diff_bit();
-      const bool flip_flag = block.get_flip_bit();
-      const uint table_index0 = block.get_inten_table(0);
-      const uint table_index1 = block.get_inten_table(1);
-
-      color_quad_u8 subblock_colors0[4];
-      color_quad_u8 subblock_colors1[4];
-      bool success = true;
-
-      if (diff_flag)
-      {
-         const uint16 base_color5 = block.get_base5_color();
-         const uint16 delta_color3 = block.get_delta3_color();
-         etc1_block::get_diff_subblock_colors(subblock_colors0, base_color5, table_index0);
-            
-         if (!etc1_block::get_diff_subblock_colors(subblock_colors1, base_color5, delta_color3, table_index1))
-            success = false;
-      }
-      else
-      {
-         const uint16 base_color4_0 = block.get_base4_color(0);
-         etc1_block::get_abs_subblock_colors(subblock_colors0, base_color4_0, table_index0);
-
-         const uint16 base_color4_1 = block.get_base4_color(1);
-         etc1_block::get_abs_subblock_colors(subblock_colors1, base_color4_1, table_index1);
-      }
-
-      if (preserve_alpha)
-      {
-         if (flip_flag)
-         {
-            for (uint y = 0; y < 2; y++)
-            {
-               pDst[0].set_rgb(subblock_colors0[block.get_selector(0, y)]);
-               pDst[1].set_rgb(subblock_colors0[block.get_selector(1, y)]);
-               pDst[2].set_rgb(subblock_colors0[block.get_selector(2, y)]);
-               pDst[3].set_rgb(subblock_colors0[block.get_selector(3, y)]);
-               pDst += 4;
-            }
-
-            for (uint y = 2; y < 4; y++)
-            {
-               pDst[0].set_rgb(subblock_colors1[block.get_selector(0, y)]);
-               pDst[1].set_rgb(subblock_colors1[block.get_selector(1, y)]);
-               pDst[2].set_rgb(subblock_colors1[block.get_selector(2, y)]);
-               pDst[3].set_rgb(subblock_colors1[block.get_selector(3, y)]);
-               pDst += 4;
-            }
-         }
-         else
-         {
-            for (uint y = 0; y < 4; y++)
-            {
-               pDst[0].set_rgb(subblock_colors0[block.get_selector(0, y)]);
-               pDst[1].set_rgb(subblock_colors0[block.get_selector(1, y)]);
-               pDst[2].set_rgb(subblock_colors1[block.get_selector(2, y)]);
-               pDst[3].set_rgb(subblock_colors1[block.get_selector(3, y)]);
-               pDst += 4;
-            }
-         }
-      }
-      else 
-      {
-         if (flip_flag)
-         {
-            // 0000
-            // 0000
-            // 1111
-            // 1111
-            for (uint y = 0; y < 2; y++)
-            {
-               pDst[0] = subblock_colors0[block.get_selector(0, y)];
-               pDst[1] = subblock_colors0[block.get_selector(1, y)];
-               pDst[2] = subblock_colors0[block.get_selector(2, y)];
-               pDst[3] = subblock_colors0[block.get_selector(3, y)];
-               pDst += 4;
-            }
-
-            for (uint y = 2; y < 4; y++)
-            {
-               pDst[0] = subblock_colors1[block.get_selector(0, y)];
-               pDst[1] = subblock_colors1[block.get_selector(1, y)];
-               pDst[2] = subblock_colors1[block.get_selector(2, y)];
-               pDst[3] = subblock_colors1[block.get_selector(3, y)];
-               pDst += 4;
-            }
-         }
-         else
-         {
-            // 0011
-            // 0011
-            // 0011
-            // 0011
-            for (uint y = 0; y < 4; y++)
-            {
-               pDst[0] = subblock_colors0[block.get_selector(0, y)];
-               pDst[1] = subblock_colors0[block.get_selector(1, y)];
-               pDst[2] = subblock_colors1[block.get_selector(2, y)];
-               pDst[3] = subblock_colors1[block.get_selector(3, y)];
-               pDst += 4;
-            }
-         }
-      }
-      
-      return success;
-   }
-
-   struct etc1_solution_coordinates
-   {
-      inline etc1_solution_coordinates() :
-      m_unscaled_color(0, 0, 0, 0),
-         m_inten_table(0),
-         m_color4(false)
-      {
-      }
-
-      inline etc1_solution_coordinates(uint r, uint g, uint b, uint inten_table, bool color4) : 
-      m_unscaled_color(r, g, b, 255),
-         m_inten_table(inten_table),
-         m_color4(color4)
-      {
-      }
-
-      inline etc1_solution_coordinates(const color_quad_u8& c, uint inten_table, bool color4) : 
-      m_unscaled_color(c),
-         m_inten_table(inten_table),
-         m_color4(color4)
-      {
-      }
-
-      inline etc1_solution_coordinates(const etc1_solution_coordinates& other)
-      {
-         *this = other;
-      }
-
-      inline etc1_solution_coordinates& operator= (const etc1_solution_coordinates& rhs)
-      {
-         m_unscaled_color = rhs.m_unscaled_color;
-         m_inten_table = rhs.m_inten_table;
-         m_color4 = rhs.m_color4;
-         return *this;
-      }
-
-      inline void clear()
-      {
-         m_unscaled_color.clear();
-         m_inten_table = 0;
-         m_color4 = false;
-      }
-
-      inline color_quad_u8 get_scaled_color() const
-      {
-         int br, bg, bb;
-         if (m_color4)
-         {
-            br = m_unscaled_color.r | (m_unscaled_color.r << 4);
-            bg = m_unscaled_color.g | (m_unscaled_color.g << 4);
-            bb = m_unscaled_color.b | (m_unscaled_color.b << 4);
-         }
-         else
-         {
-            br = (m_unscaled_color.r >> 2) | (m_unscaled_color.r << 3);
-            bg = (m_unscaled_color.g >> 2) | (m_unscaled_color.g << 3);
-            bb = (m_unscaled_color.b >> 2) | (m_unscaled_color.b << 3);
-         }
-         return color_quad_u8(br, bg, bb);
-      }
-
-      inline void get_block_colors(color_quad_u8* pBlock_colors)
-      {
-         int br, bg, bb;
-         if (m_color4)
-         {
-            br = m_unscaled_color.r | (m_unscaled_color.r << 4);
-            bg = m_unscaled_color.g | (m_unscaled_color.g << 4);
-            bb = m_unscaled_color.b | (m_unscaled_color.b << 4);
-         }
-         else
-         {
-            br = (m_unscaled_color.r >> 2) | (m_unscaled_color.r << 3);
-            bg = (m_unscaled_color.g >> 2) | (m_unscaled_color.g << 3);
-            bb = (m_unscaled_color.b >> 2) | (m_unscaled_color.b << 3);
-         }
-         const int* pInten_table = g_etc1_inten_tables[m_inten_table];
-         pBlock_colors[0].set(br + pInten_table[0], bg + pInten_table[0], bb + pInten_table[0]);
-         pBlock_colors[1].set(br + pInten_table[1], bg + pInten_table[1], bb + pInten_table[1]);
-         pBlock_colors[2].set(br + pInten_table[2], bg + pInten_table[2], bb + pInten_table[2]);
-         pBlock_colors[3].set(br + pInten_table[3], bg + pInten_table[3], bb + pInten_table[3]);
-      }
-
-      color_quad_u8 m_unscaled_color;
-      uint m_inten_table;
-      bool m_color4;
-   };
-
-   class etc1_optimizer
-   {
-      etc1_optimizer(const etc1_optimizer&);
-      etc1_optimizer& operator= (const etc1_optimizer&);
-
-   public:
-      etc1_optimizer()
-      {
-         clear();
-      }
-
-      void clear()
-      {
-         m_pParams = NULL;
-         m_pResult = NULL;
-         m_pSorted_luma = NULL;
-         m_pSorted_luma_indices = NULL;
-      }
-
-      struct params : etc1_pack_params
-      {
-         params()
-         {
-            clear();
-         }
-
-         params(const etc1_pack_params& base_params) : 
-         etc1_pack_params(base_params)
-         {
-            clear_optimizer_params();
-         }
-
-         void clear()
-         {
-            etc1_pack_params::clear();
-            clear_optimizer_params();
-         }
-
-         void clear_optimizer_params()
-         {
-            m_num_src_pixels = 0;
-            m_pSrc_pixels = 0;
-
-            m_use_color4 = false;
-            static const int s_default_scan_delta[] = { 0 };
-            m_pScan_deltas = s_default_scan_delta;
-            m_scan_delta_size = 1;
-
-            m_base_color5.clear();
-            m_constrain_against_base_color5 = false;
-         }
-
-         uint m_num_src_pixels;
-         const color_quad_u8* m_pSrc_pixels;
-
-         bool m_use_color4;
-         const int* m_pScan_deltas;
-         uint m_scan_delta_size;
-
-         color_quad_u8 m_base_color5;
-         bool m_constrain_against_base_color5;
-      };
-
-      struct results
-      {
-         uint64 m_error;
-         color_quad_u8 m_block_color_unscaled;
-         uint m_block_inten_table;
-         uint m_n;
-         uint8* m_pSelectors;
-         bool m_block_color4;
-
-         inline results& operator= (const results& rhs)
-         {
-            m_block_color_unscaled = rhs.m_block_color_unscaled;
-            m_block_color4 = rhs.m_block_color4;
-            m_block_inten_table = rhs.m_block_inten_table;
-            m_error = rhs.m_error;
-            RG_ETC1_ASSERT(m_n == rhs.m_n);
-            memcpy(m_pSelectors, rhs.m_pSelectors, rhs.m_n);
-            return *this;
-         }
-      };
-
-      void init(const params& params, results& result);
-      bool compute();
-
-   private:      
-      struct potential_solution
-      {
-         potential_solution() : m_coords(), m_error(cUINT64_MAX), m_valid(false)
-         {
-         }
-
-         etc1_solution_coordinates  m_coords;
-         uint8                      m_selectors[8];
-         uint64                     m_error;
-         bool                       m_valid;
-
-         void clear()
-         {
-            m_coords.clear();
-            m_error = cUINT64_MAX;
-            m_valid = false;
-         }
-      };
-
-      const params* m_pParams;
-      results* m_pResult;
-
-      int m_limit;
-
-      vec3F m_avg_color;
-      int m_br, m_bg, m_bb;
-      uint16 m_luma[8];
-      uint32 m_sorted_luma[2][8];
-      const uint32* m_pSorted_luma_indices;
-      uint32* m_pSorted_luma;
-
-      uint8 m_selectors[8];
-      uint8 m_best_selectors[8];
-
-      potential_solution m_best_solution;
-      potential_solution m_trial_solution;
-      uint8 m_temp_selectors[8];
-
-      bool evaluate_solution(const etc1_solution_coordinates& coords, potential_solution& trial_solution, potential_solution* pBest_solution);
-      bool evaluate_solution_fast(const etc1_solution_coordinates& coords, potential_solution& trial_solution, potential_solution* pBest_solution);
-   };
-      
-   bool etc1_optimizer::compute()
-   {
-      const uint n = m_pParams->m_num_src_pixels;
-      const int scan_delta_size = m_pParams->m_scan_delta_size;
-      
-      // Scan through a subset of the 3D lattice centered around the avg block color trying each 3D (555 or 444) lattice point as a potential block color.
-      // Each time a better solution is found try to refine the current solution's block color based of the current selectors and intensity table index.
-      for (int zdi = 0; zdi < scan_delta_size; zdi++)
-      {
-         const int zd = m_pParams->m_pScan_deltas[zdi];
-         const int mbb = m_bb + zd;
-         if (mbb < 0) continue; else if (mbb > m_limit) break;
-         
-         for (int ydi = 0; ydi < scan_delta_size; ydi++)
-         {
-            const int yd = m_pParams->m_pScan_deltas[ydi];
-            const int mbg = m_bg + yd;
-            if (mbg < 0) continue; else if (mbg > m_limit) break;
-
-            for (int xdi = 0; xdi < scan_delta_size; xdi++)
-            {
-               const int xd = m_pParams->m_pScan_deltas[xdi];
-               const int mbr = m_br + xd;
-               if (mbr < 0) continue; else if (mbr > m_limit) break;
-      
-               etc1_solution_coordinates coords(mbr, mbg, mbb, 0, m_pParams->m_use_color4);
-               if (m_pParams->m_quality == cHighQuality)
-               {
-                  if (!evaluate_solution(coords, m_trial_solution, &m_best_solution))
-                     continue;
-               }
-               else
-               {
-                  if (!evaluate_solution_fast(coords, m_trial_solution, &m_best_solution))
-                     continue;
-               }
-               
-               // Now we have the input block, the avg. color of the input pixels, a set of trial selector indices, and the block color+intensity index.
-               // Now, for each component, attempt to refine the current solution by solving a simple linear equation. For example, for 4 colors:
-               // The goal is:
-               // pixel0 - (block_color+inten_table[selector0]) + pixel1 - (block_color+inten_table[selector1]) + pixel2 - (block_color+inten_table[selector2]) + pixel3 - (block_color+inten_table[selector3]) = 0
-               // Rearranging this:
-               // (pixel0 + pixel1 + pixel2 + pixel3) - (block_color+inten_table[selector0]) - (block_color+inten_table[selector1]) - (block_color+inten_table[selector2]) - (block_color+inten_table[selector3]) = 0
-               // (pixel0 + pixel1 + pixel2 + pixel3) - block_color - inten_table[selector0] - block_color-inten_table[selector1] - block_color-inten_table[selector2] - block_color-inten_table[selector3] = 0
-               // (pixel0 + pixel1 + pixel2 + pixel3) - 4*block_color - inten_table[selector0] - inten_table[selector1] - inten_table[selector2] - inten_table[selector3] = 0
-               // (pixel0 + pixel1 + pixel2 + pixel3) - 4*block_color - (inten_table[selector0] + inten_table[selector1] + inten_table[selector2] + inten_table[selector3]) = 0
-               // (pixel0 + pixel1 + pixel2 + pixel3)/4 - block_color - (inten_table[selector0] + inten_table[selector1] + inten_table[selector2] + inten_table[selector3])/4 = 0
-               // block_color = (pixel0 + pixel1 + pixel2 + pixel3)/4 - (inten_table[selector0] + inten_table[selector1] + inten_table[selector2] + inten_table[selector3])/4
-               // So what this means:
-               // optimal_block_color = avg_input - avg_inten_delta
-               // So the optimal block color can be computed by taking the average block color and subtracting the current average of the intensity delta.
-               // Unfortunately, optimal_block_color must then be quantized to 555 or 444 so it's not always possible to improve matters using this formula.
-               // Also, the above formula is for unclamped intensity deltas. The actual implementation takes into account clamping.
-
-               const uint max_refinement_trials = (m_pParams->m_quality == cLowQuality) ? 2 : (((xd | yd | zd) == 0) ? 4 : 2);
-               for (uint refinement_trial = 0; refinement_trial < max_refinement_trials; refinement_trial++)
-               {
-                  const uint8* pSelectors = m_best_solution.m_selectors;
-                  const int* pInten_table = g_etc1_inten_tables[m_best_solution.m_coords.m_inten_table];
-
-                  int delta_sum_r = 0, delta_sum_g = 0, delta_sum_b = 0;
-                  const color_quad_u8 base_color(m_best_solution.m_coords.get_scaled_color());
-                  for (uint r = 0; r < n; r++)
-                  {
-                     const uint s = *pSelectors++;
-                     const int yd = pInten_table[s];
-                     // Compute actual delta being applied to each pixel, taking into account clamping.
-                     delta_sum_r += rg_etc1::clamp<int>(base_color.r + yd, 0, 255) - base_color.r;
-                     delta_sum_g += rg_etc1::clamp<int>(base_color.g + yd, 0, 255) - base_color.g;
-                     delta_sum_b += rg_etc1::clamp<int>(base_color.b + yd, 0, 255) - base_color.b;
-                  }
-                  if ((!delta_sum_r) && (!delta_sum_g) && (!delta_sum_b))
-                     break;
-                  const float avg_delta_r_f = static_cast<float>(delta_sum_r) / n;
-                  const float avg_delta_g_f = static_cast<float>(delta_sum_g) / n;
-                  const float avg_delta_b_f = static_cast<float>(delta_sum_b) / n;
-                  const int br1 = rg_etc1::clamp<int>(static_cast<uint>((m_avg_color[0] - avg_delta_r_f) * m_limit / 255.0f + .5f), 0, m_limit);
-                  const int bg1 = rg_etc1::clamp<int>(static_cast<uint>((m_avg_color[1] - avg_delta_g_f) * m_limit / 255.0f + .5f), 0, m_limit);
-                  const int bb1 = rg_etc1::clamp<int>(static_cast<uint>((m_avg_color[2] - avg_delta_b_f) * m_limit / 255.0f + .5f), 0, m_limit);
-                  
-                  bool skip = false;
-                  
-                  if ((mbr == br1) && (mbg == bg1) && (mbb == bb1))
-                     skip = true;
-                  else if ((br1 == m_best_solution.m_coords.m_unscaled_color.r) && (bg1 == m_best_solution.m_coords.m_unscaled_color.g) && (bb1 == m_best_solution.m_coords.m_unscaled_color.b))
-                     skip = true;
-                  else if ((m_br == br1) && (m_bg == bg1) && (m_bb == bb1))
-                     skip = true;
-
-                  if (skip)
-                     break;
-
-                  etc1_solution_coordinates coords1(br1, bg1, bb1, 0, m_pParams->m_use_color4);
-                  if (m_pParams->m_quality == cHighQuality)
-                  {
-                     if (!evaluate_solution(coords1, m_trial_solution, &m_best_solution)) 
-                        break;
-                  }
-                  else
-                  {
-                     if (!evaluate_solution_fast(coords1, m_trial_solution, &m_best_solution))
-                        break;
-                  }
-
-               }  // refinement_trial
-
-            } // xdi
-         } // ydi
-      } // zdi
-
-      if (!m_best_solution.m_valid)
-      {
-         m_pResult->m_error = cUINT32_MAX;
-         return false;
-      }
-      
-      const uint8* pSelectors = m_best_solution.m_selectors;
-
-#ifdef RG_ETC1_BUILD_DEBUG
-      {
-         color_quad_u8 block_colors[4];
-         m_best_solution.m_coords.get_block_colors(block_colors);
-
-         const color_quad_u8* pSrc_pixels = m_pParams->m_pSrc_pixels;
-         uint64 actual_error = 0;
-         for (uint i = 0; i < n; i++)
-            actual_error += pSrc_pixels[i].squared_distance_rgb(block_colors[pSelectors[i]]);
-         
-         RG_ETC1_ASSERT(actual_error == m_best_solution.m_error);
-      }
-#endif      
-      
-      m_pResult->m_error = m_best_solution.m_error;
-
-      m_pResult->m_block_color_unscaled = m_best_solution.m_coords.m_unscaled_color;
-      m_pResult->m_block_color4 = m_best_solution.m_coords.m_color4;
-      
-      m_pResult->m_block_inten_table = m_best_solution.m_coords.m_inten_table;
-      memcpy(m_pResult->m_pSelectors, pSelectors, n);
-      m_pResult->m_n = n;
-
-      return true;
-   }
-
-   void etc1_optimizer::init(const params& p, results& r)
-   {
-      // This version is hardcoded for 8 pixel subblocks.
-      RG_ETC1_ASSERT(p.m_num_src_pixels == 8);
-      
-      m_pParams = &p;
-      m_pResult = &r;
-                  
-      const uint n = 8;
-      
-      m_limit = m_pParams->m_use_color4 ? 15 : 31;
-
-      vec3F avg_color(0.0f);
-
-      for (uint i = 0; i < n; i++)
-      {
-         const color_quad_u8& c = m_pParams->m_pSrc_pixels[i];
-         const vec3F fc(c.r, c.g, c.b);
-
-         avg_color += fc;
-
-         m_luma[i] = static_cast<uint16>(c.r + c.g + c.b);
-         m_sorted_luma[0][i] = i;
-      }
-      avg_color *= (1.0f / static_cast<float>(n));
-      m_avg_color = avg_color;
-
-      m_br = rg_etc1::clamp<int>(static_cast<uint>(m_avg_color[0] * m_limit / 255.0f + .5f), 0, m_limit);
-      m_bg = rg_etc1::clamp<int>(static_cast<uint>(m_avg_color[1] * m_limit / 255.0f + .5f), 0, m_limit);
-      m_bb = rg_etc1::clamp<int>(static_cast<uint>(m_avg_color[2] * m_limit / 255.0f + .5f), 0, m_limit);
-
-      if (m_pParams->m_quality <= cMediumQuality)
-      {
-         m_pSorted_luma_indices = indirect_radix_sort(n, m_sorted_luma[0], m_sorted_luma[1], m_luma, 0, sizeof(m_luma[0]), false);
-         m_pSorted_luma = m_sorted_luma[0];
-         if (m_pSorted_luma_indices == m_sorted_luma[0])
-            m_pSorted_luma = m_sorted_luma[1];
-      
-         for (uint i = 0; i < n; i++)
-            m_pSorted_luma[i] = m_luma[m_pSorted_luma_indices[i]];
-      }
-      
-      m_best_solution.m_coords.clear();
-      m_best_solution.m_valid = false;
-      m_best_solution.m_error = cUINT64_MAX;
-   }
-
-   bool etc1_optimizer::evaluate_solution(const etc1_solution_coordinates& coords, potential_solution& trial_solution, potential_solution* pBest_solution)
-   {
-      trial_solution.m_valid = false;
-
-      if (m_pParams->m_constrain_against_base_color5)
-      {
-         const int dr = coords.m_unscaled_color.r - m_pParams->m_base_color5.r;
-         const int dg = coords.m_unscaled_color.g - m_pParams->m_base_color5.g;
-         const int db = coords.m_unscaled_color.b - m_pParams->m_base_color5.b;
-
-         if ((rg_etc1::minimum(dr, dg, db) < cETC1ColorDeltaMin) || (rg_etc1::maximum(dr, dg, db) > cETC1ColorDeltaMax))
-            return false;
-      }
-
-      const color_quad_u8 base_color(coords.get_scaled_color());
-      
-      const uint n = 8;
-            
-      trial_solution.m_error = cUINT64_MAX;
-            
-      for (uint inten_table = 0; inten_table < cETC1IntenModifierValues; inten_table++)
-      {
-         const int* pInten_table = g_etc1_inten_tables[inten_table];
-
-         color_quad_u8 block_colors[4];
-         for (uint s = 0; s < 4; s++)
-         {
-            const int yd = pInten_table[s];
-            block_colors[s].set(base_color.r + yd, base_color.g + yd, base_color.b + yd, 0);
-         }
-         
-         uint64 total_error = 0;
-         
-         const color_quad_u8* pSrc_pixels = m_pParams->m_pSrc_pixels;
-         for (uint c = 0; c < n; c++)
-         {
-            const color_quad_u8& src_pixel = *pSrc_pixels++;
-            
-            uint best_selector_index = 0;
-            uint best_error = rg_etc1::square(src_pixel.r - block_colors[0].r) + rg_etc1::square(src_pixel.g - block_colors[0].g) + rg_etc1::square(src_pixel.b - block_colors[0].b);
-
-            uint trial_error = rg_etc1::square(src_pixel.r - block_colors[1].r) + rg_etc1::square(src_pixel.g - block_colors[1].g) + rg_etc1::square(src_pixel.b - block_colors[1].b);
-            if (trial_error < best_error)
-            {
-               best_error = trial_error;
-               best_selector_index = 1;
-            }
-
-            trial_error = rg_etc1::square(src_pixel.r - block_colors[2].r) + rg_etc1::square(src_pixel.g - block_colors[2].g) + rg_etc1::square(src_pixel.b - block_colors[2].b);
-            if (trial_error < best_error)
-            {
-               best_error = trial_error;
-               best_selector_index = 2;
-            }
-
-            trial_error = rg_etc1::square(src_pixel.r - block_colors[3].r) + rg_etc1::square(src_pixel.g - block_colors[3].g) + rg_etc1::square(src_pixel.b - block_colors[3].b);
-            if (trial_error < best_error)
-            {
-               best_error = trial_error;
-               best_selector_index = 3;
-            }
-
-            m_temp_selectors[c] = static_cast<uint8>(best_selector_index);
-
-            total_error += best_error;
-            if (total_error >= trial_solution.m_error)
-               break;
-         }
-         
-         if (total_error < trial_solution.m_error)
-         {
-            trial_solution.m_error = total_error;
-            trial_solution.m_coords.m_inten_table = inten_table;
-            memcpy(trial_solution.m_selectors, m_temp_selectors, 8);
-            trial_solution.m_valid = true;
-         }
-      }
-      trial_solution.m_coords.m_unscaled_color = coords.m_unscaled_color;
-      trial_solution.m_coords.m_color4 = m_pParams->m_use_color4;
-
-      bool success = false;
-      if (pBest_solution)
-      {
-         if (trial_solution.m_error < pBest_solution->m_error)
-         {
-            *pBest_solution = trial_solution;
-            success = true;
-         }
-      }
-
-      return success;
-   }
-
-   bool etc1_optimizer::evaluate_solution_fast(const etc1_solution_coordinates& coords, potential_solution& trial_solution, potential_solution* pBest_solution)
-   {
-      if (m_pParams->m_constrain_against_base_color5)
-      {
-         const int dr = coords.m_unscaled_color.r - m_pParams->m_base_color5.r;
-         const int dg = coords.m_unscaled_color.g - m_pParams->m_base_color5.g;
-         const int db = coords.m_unscaled_color.b - m_pParams->m_base_color5.b;
-
-         if ((rg_etc1::minimum(dr, dg, db) < cETC1ColorDeltaMin) || (rg_etc1::maximum(dr, dg, db) > cETC1ColorDeltaMax))
-         {
-            trial_solution.m_valid = false;
-            return false;
-         }
-      }
-
-      const color_quad_u8 base_color(coords.get_scaled_color());
-
-      const uint n = 8;
-      
-      trial_solution.m_error = cUINT64_MAX;
-
-      for (int inten_table = cETC1IntenModifierValues - 1; inten_table >= 0; --inten_table)
-      {
-         const int* pInten_table = g_etc1_inten_tables[inten_table];
-
-         uint block_inten[4];
-         color_quad_u8 block_colors[4];
-         for (uint s = 0; s < 4; s++)
-         {
-            const int yd = pInten_table[s];
-            color_quad_u8 block_color(base_color.r + yd, base_color.g + yd, base_color.b + yd, 0);
-            block_colors[s] = block_color;
-            block_inten[s] = block_color.r + block_color.g + block_color.b;
-         }
-
-         // evaluate_solution_fast() enforces/assumesd a total ordering of the input colors along the intensity (1,1,1) axis to more quickly classify the inputs to selectors.
-         // The inputs colors have been presorted along the projection onto this axis, and ETC1 block colors are always ordered along the intensity axis, so this classification is fast.
-         // 0   1   2   3
-         //   01  12  23
-         const uint block_inten_midpoints[3] = { block_inten[0] + block_inten[1], block_inten[1] + block_inten[2], block_inten[2] + block_inten[3] };
-
-         uint64 total_error = 0;
-         const color_quad_u8* pSrc_pixels = m_pParams->m_pSrc_pixels;
-         if ((m_pSorted_luma[n - 1] * 2) < block_inten_midpoints[0])
-         {
-            if (block_inten[0] > m_pSorted_luma[n - 1])
-            {
-               const uint min_error = labs(block_inten[0] - m_pSorted_luma[n - 1]);
-               if (min_error >= trial_solution.m_error)
-                  continue;
-            }
-
-            memset(&m_temp_selectors[0], 0, n);
-
-            for (uint c = 0; c < n; c++)
-               total_error += block_colors[0].squared_distance_rgb(pSrc_pixels[c]);
-         }
-         else if ((m_pSorted_luma[0] * 2) >= block_inten_midpoints[2])
-         {
-            if (m_pSorted_luma[0] > block_inten[3])
-            {
-               const uint min_error = labs(m_pSorted_luma[0] - block_inten[3]);
-               if (min_error >= trial_solution.m_error)
-                  continue;
-            }
-
-            memset(&m_temp_selectors[0], 3, n);
-
-            for (uint c = 0; c < n; c++)
-               total_error += block_colors[3].squared_distance_rgb(pSrc_pixels[c]);
-         }
-         else
-         {
-            uint cur_selector = 0, c;
-            for (c = 0; c < n; c++)
-            {
-               const uint y = m_pSorted_luma[c];
-               while ((y * 2) >= block_inten_midpoints[cur_selector])
-                  if (++cur_selector > 2)
-                     goto done;
-               const uint sorted_pixel_index = m_pSorted_luma_indices[c];
-               m_temp_selectors[sorted_pixel_index] = static_cast<uint8>(cur_selector);
-               total_error += block_colors[cur_selector].squared_distance_rgb(pSrc_pixels[sorted_pixel_index]);
-            }
-done:
-            while (c < n)
-            {
-               const uint sorted_pixel_index = m_pSorted_luma_indices[c];
-               m_temp_selectors[sorted_pixel_index] = 3;
-               total_error += block_colors[3].squared_distance_rgb(pSrc_pixels[sorted_pixel_index]);
-               ++c;
-            }
-         }
-
-         if (total_error < trial_solution.m_error)
-         {
-            trial_solution.m_error = total_error;
-            trial_solution.m_coords.m_inten_table = inten_table;
-            memcpy(trial_solution.m_selectors, m_temp_selectors, n);
-            trial_solution.m_valid = true;
-            if (!total_error)
-               break;
-         }
-      }
-      trial_solution.m_coords.m_unscaled_color = coords.m_unscaled_color;
-      trial_solution.m_coords.m_color4 = m_pParams->m_use_color4;
-      
-      bool success = false;
-      if (pBest_solution)
-      {
-         if (trial_solution.m_error < pBest_solution->m_error)
-         {
-            *pBest_solution = trial_solution;
-            success = true;
-         }
-      }
-
-      return success;
-   }
-         
-   static uint etc1_decode_value(uint diff, uint inten, uint selector, uint packed_c)
-   {
-      const uint limit = diff ? 32 : 16; limit;
-      RG_ETC1_ASSERT((diff < 2) && (inten < 8) && (selector < 4) && (packed_c < limit));
-      int c;
-      if (diff)
-         c = (packed_c >> 2) | (packed_c << 3);
-      else 
-         c = packed_c | (packed_c << 4);
-      c += g_etc1_inten_tables[inten][selector];
-      c = rg_etc1::clamp<int>(c, 0, 255);
-      return c;
-   }
-
-   static inline int mul_8bit(int a, int b) { int t = a*b + 128; return (t + (t >> 8)) >> 8; }
-
-   void pack_etc1_block_init()
-   {
-      for (uint diff = 0; diff < 2; diff++)
-      {
-         const uint limit = diff ? 32 : 16;
-
-         for (uint inten = 0; inten < 8; inten++)
-         {
-            for (uint selector = 0; selector < 4; selector++)
-            {
-               const uint inverse_table_index = diff + (inten << 1) + (selector << 4);
-               for (uint color = 0; color < 256; color++)
-               {
-                  uint best_error = cUINT32_MAX, best_packed_c = 0;
-                  for (uint packed_c = 0; packed_c < limit; packed_c++)
-                  {
-                     int v = etc1_decode_value(diff, inten, selector, packed_c);
-                     uint err = labs(v - static_cast<int>(color));
-                     if (err < best_error)
-                     {
-                        best_error = err;
-                        best_packed_c = packed_c;
-                        if (!best_error) 
-                           break;
-                     }
-                  }
-                  RG_ETC1_ASSERT(best_error <= 255);
-                  g_etc1_inverse_lookup[inverse_table_index][color] = static_cast<uint16>(best_packed_c | (best_error << 8));
-               }
-            }
-         }
-      }
-      
-      uint expand5[32];
-      for(int i = 0; i < 32; i++)
-         expand5[i] = (i << 3) | (i >> 2);
-
-      for(int i = 0; i < 256 + 16; i++)
-      {
-         int v = clamp<int>(i - 8, 0, 255);
-         g_quant5_tab[i] = static_cast<uint8>(expand5[mul_8bit(v,31)]);
-      }
-   }
-
-   // Packs solid color blocks efficiently using a set of small precomputed tables.
-   // For random 888 inputs, MSE results are better than Erricson's ETC1 packer in "slow" mode ~9.5% of the time, is slightly worse only ~.01% of the time, and is equal the rest of the time.
-   static uint64 pack_etc1_block_solid_color(etc1_block& block, const uint8* pColor, etc1_pack_params& pack_params)
-   {
-      pack_params;
-      RG_ETC1_ASSERT(g_etc1_inverse_lookup[0][255]);
-            
-      static uint s_next_comp[4] = { 1, 2, 0, 1 };
-            
-      uint best_error = cUINT32_MAX, best_i = 0;
-      int best_x = 0, best_packed_c1 = 0, best_packed_c2 = 0;
-
-      // For each possible 8-bit value, there is a precomputed list of diff/inten/selector configurations that allow that 8-bit value to be encoded with no error.
-      for (uint i = 0; i < 3; i++)
-      {
-         const uint c1 = pColor[s_next_comp[i]], c2 = pColor[s_next_comp[i + 1]];
-
-         const int delta_range = 1;
-         for (int delta = -delta_range; delta <= delta_range; delta++)
-         {
-            const int c_plus_delta = rg_etc1::clamp<int>(pColor[i] + delta, 0, 255);
-
-            const uint16* pTable;
-            if (!c_plus_delta)
-               pTable = g_color8_to_etc_block_config_0_255[0];
-            else if (c_plus_delta == 255)
-               pTable = g_color8_to_etc_block_config_0_255[1];
-            else
-               pTable = g_color8_to_etc_block_config_1_to_254[c_plus_delta - 1];
-
-            do
-            {
-               const uint x = *pTable++;
-
-#ifdef RG_ETC1_BUILD_DEBUG
-               const uint diff = x & 1;
-               const uint inten = (x >> 1) & 7;
-               const uint selector = (x >> 4) & 3;
-               const uint p0 = (x >> 8) & 255;
-               RG_ETC1_ASSERT(etc1_decode_value(diff, inten, selector, p0) == (uint)c_plus_delta);
-#endif
-
-               const uint16* pInverse_table = g_etc1_inverse_lookup[x & 0xFF];
-               uint16 p1 = pInverse_table[c1];
-               uint16 p2 = pInverse_table[c2];
-               const uint trial_error = rg_etc1::square(c_plus_delta - pColor[i]) + rg_etc1::square(p1 >> 8) + rg_etc1::square(p2 >> 8);
-               if (trial_error < best_error)
-               {
-                  best_error = trial_error;
-                  best_x = x;
-                  best_packed_c1 = p1 & 0xFF;
-                  best_packed_c2 = p2 & 0xFF;
-                  best_i = i;
-                  if (!best_error)
-                     goto found_perfect_match;
-               }
-            } while (*pTable != 0xFFFF);
-         }
-      }
-found_perfect_match:
-
-      const uint diff = best_x & 1;
-      const uint inten = (best_x >> 1) & 7;
-
-      block.m_bytes[3] = static_cast<uint8>(((inten | (inten << 3)) << 2) | (diff << 1));
-                        
-      const uint etc1_selector = g_selector_index_to_etc1[(best_x >> 4) & 3];
-      *reinterpret_cast<uint16*>(&block.m_bytes[4]) = (etc1_selector & 2) ? 0xFFFF : 0;
-      *reinterpret_cast<uint16*>(&block.m_bytes[6]) = (etc1_selector & 1) ? 0xFFFF : 0;
-
-      const uint best_packed_c0 = (best_x >> 8) & 255;
-      if (diff)
-      {
-         block.m_bytes[best_i] = static_cast<uint8>(best_packed_c0 << 3);
-         block.m_bytes[s_next_comp[best_i]] = static_cast<uint8>(best_packed_c1 << 3);
-         block.m_bytes[s_next_comp[best_i+1]] = static_cast<uint8>(best_packed_c2 << 3);
-      }
-      else
-      {
-         block.m_bytes[best_i] = static_cast<uint8>(best_packed_c0 | (best_packed_c0 << 4));
-         block.m_bytes[s_next_comp[best_i]] = static_cast<uint8>(best_packed_c1 | (best_packed_c1 << 4));
-         block.m_bytes[s_next_comp[best_i+1]] = static_cast<uint8>(best_packed_c2 | (best_packed_c2 << 4));
-      }
-
-      return best_error;
-   }
-      
-   static uint pack_etc1_block_solid_color_constrained(
-      etc1_optimizer::results& results, 
-      uint num_colors, const uint8* pColor, 
-      etc1_pack_params& pack_params, 
-      bool use_diff,
-      const color_quad_u8* pBase_color5_unscaled)
-   {
-      RG_ETC1_ASSERT(g_etc1_inverse_lookup[0][255]);
-
-      pack_params;
-      static uint s_next_comp[4] = { 1, 2, 0, 1 };
-
-      uint best_error = cUINT32_MAX, best_i = 0;
-      int best_x = 0, best_packed_c1 = 0, best_packed_c2 = 0;
-
-      // For each possible 8-bit value, there is a precomputed list of diff/inten/selector configurations that allow that 8-bit value to be encoded with no error.
-      for (uint i = 0; i < 3; i++)
-      {
-         const uint c1 = pColor[s_next_comp[i]], c2 = pColor[s_next_comp[i + 1]];
-
-         const int delta_range = 1;
-         for (int delta = -delta_range; delta <= delta_range; delta++)
-         {
-            const int c_plus_delta = rg_etc1::clamp<int>(pColor[i] + delta, 0, 255);
-
-            const uint16* pTable;
-            if (!c_plus_delta)
-               pTable = g_color8_to_etc_block_config_0_255[0];
-            else if (c_plus_delta == 255)
-               pTable = g_color8_to_etc_block_config_0_255[1];
-            else
-               pTable = g_color8_to_etc_block_config_1_to_254[c_plus_delta - 1];
-
-            do
-            {
-               const uint x = *pTable++;
-               const uint diff = x & 1;
-               if (static_cast<uint>(use_diff) != diff)
-               {
-                  if (*pTable == 0xFFFF)
-                     break;
-                  continue;
-               }
-
-               if ((diff) && (pBase_color5_unscaled))
-               {
-                  const int p0 = (x >> 8) & 255;
-                  int delta = p0 - static_cast<int>(pBase_color5_unscaled->c[i]);
-                  if ((delta < cETC1ColorDeltaMin) || (delta > cETC1ColorDeltaMax))
-                  {
-                     if (*pTable == 0xFFFF)
-                        break;
-                     continue;
-                  }
-               }
-
-#ifdef RG_ETC1_BUILD_DEBUG
-               {
-                  const uint inten = (x >> 1) & 7;
-                  const uint selector = (x >> 4) & 3;
-                  const uint p0 = (x >> 8) & 255;
-                  RG_ETC1_ASSERT(etc1_decode_value(diff, inten, selector, p0) == (uint)c_plus_delta);
-               }
-#endif
-
-               const uint16* pInverse_table = g_etc1_inverse_lookup[x & 0xFF];
-               uint16 p1 = pInverse_table[c1];
-               uint16 p2 = pInverse_table[c2];
-
-               if ((diff) && (pBase_color5_unscaled))
-               {
-                  int delta1 = (p1 & 0xFF) - static_cast<int>(pBase_color5_unscaled->c[s_next_comp[i]]);
-                  int delta2 = (p2 & 0xFF) - static_cast<int>(pBase_color5_unscaled->c[s_next_comp[i + 1]]);
-                  if ((delta1 < cETC1ColorDeltaMin) || (delta1 > cETC1ColorDeltaMax) || (delta2 < cETC1ColorDeltaMin) || (delta2 > cETC1ColorDeltaMax))
-                  {
-                     if (*pTable == 0xFFFF)
-                        break;
-                     continue;
-                  }
-               }
-
-               const uint trial_error = rg_etc1::square(c_plus_delta - pColor[i]) + rg_etc1::square(p1 >> 8) + rg_etc1::square(p2 >> 8);
-               if (trial_error < best_error)
-               {
-                  best_error = trial_error;
-                  best_x = x;
-                  best_packed_c1 = p1 & 0xFF;
-                  best_packed_c2 = p2 & 0xFF;
-                  best_i = i;
-                  if (!best_error)
-                     goto found_perfect_match;
-               }
-            } while (*pTable != 0xFFFF);
-         }
-      }
-found_perfect_match:
-
-      if (best_error == cUINT32_MAX)
-         return best_error;
-
-      best_error *= num_colors;
-
-      results.m_n = num_colors;
-      results.m_block_color4 = !(best_x & 1);
-      results.m_block_inten_table = (best_x >> 1) & 7;
-      memset(results.m_pSelectors, (best_x >> 4) & 3, num_colors);
-
-      const uint best_packed_c0 = (best_x >> 8) & 255;
-      results.m_block_color_unscaled[best_i] = static_cast<uint8>(best_packed_c0);
-      results.m_block_color_unscaled[s_next_comp[best_i]] = static_cast<uint8>(best_packed_c1);
-      results.m_block_color_unscaled[s_next_comp[best_i + 1]] = static_cast<uint8>(best_packed_c2);
-      results.m_error = best_error;
-      
-      return best_error;
-   }
-
-   // Function originally from RYG's public domain real-time DXT1 compressor, modified for 555.
-   static void dither_block_555(color_quad_u8* dest, const color_quad_u8* block)
-   {
-      int err[8],*ep1 = err,*ep2 = err+4;
-      uint8 *quant = g_quant5_tab+8;
-
-      memset(dest, 0xFF, sizeof(color_quad_u8)*16);
-
-      // process channels seperately
-      for(int ch=0;ch<3;ch++)
-      {
-         uint8* bp = (uint8*)block;
-         uint8* dp = (uint8*)dest;
-
-         bp += ch; dp += ch;
-
-         memset(err,0, sizeof(err));
-         for(int y = 0; y < 4; y++)
-         {
-            // pixel 0
-            dp[ 0] = quant[bp[ 0] + ((3*ep2[1] + 5*ep2[0]) >> 4)];
-            ep1[0] = bp[ 0] - dp[ 0];
-
-            // pixel 1
-            dp[ 4] = quant[bp[ 4] + ((7*ep1[0] + 3*ep2[2] + 5*ep2[1] + ep2[0]) >> 4)];
-            ep1[1] = bp[ 4] - dp[ 4];
-
-            // pixel 2
-            dp[ 8] = quant[bp[ 8] + ((7*ep1[1] + 3*ep2[3] + 5*ep2[2] + ep2[1]) >> 4)];
-            ep1[2] = bp[ 8] - dp[ 8];
-
-            // pixel 3
-            dp[12] = quant[bp[12] + ((7*ep1[2] + 5*ep2[3] + ep2[2]) >> 4)];
-            ep1[3] = bp[12] - dp[12];
-
-            // advance to next line
-            int* tmp = ep1; ep1 = ep2; ep2 = tmp;
-            bp += 16;
-            dp += 16;
-         }
-      }
-   }
-
-   unsigned int pack_etc1_block(void* pETC1_block, const unsigned int* pSrc_pixels_rgba, etc1_pack_params& pack_params)
-   {
-      const color_quad_u8* pSrc_pixels = reinterpret_cast<const color_quad_u8*>(pSrc_pixels_rgba);
-      etc1_block& dst_block = *static_cast<etc1_block*>(pETC1_block);
-
-#ifdef RG_ETC1_BUILD_DEBUG
-      // Ensure all alpha values are 0xFF.
-      for (uint i = 0; i < 16; i++)
-      {
-         RG_ETC1_ASSERT(pSrc_pixels[i].a == 255);
-      }
-#endif
-
-      color_quad_u8 src_pixel0(pSrc_pixels[0]);
-
-      // Check for solid block.
-      const uint32 first_pixel_u32 = pSrc_pixels->m_u32;
-      int r;
-      for (r = 15; r >= 1; --r)
-         if (pSrc_pixels[r].m_u32 != first_pixel_u32)
-            break;
-      if (!r)
-         return static_cast<unsigned int>(16 * pack_etc1_block_solid_color(dst_block, &pSrc_pixels[0].r, pack_params));
-      
-      color_quad_u8 dithered_pixels[16];
-      if (pack_params.m_dithering)
-      {
-         dither_block_555(dithered_pixels, pSrc_pixels);
-         pSrc_pixels = dithered_pixels;
-      }
-
-      etc1_optimizer optimizer;
-
-      uint64 best_error = cUINT64_MAX;
-      uint best_flip = false, best_use_color4 = false;
-      
-      uint8 best_selectors[2][8];
-      etc1_optimizer::results best_results[2];
-      for (uint i = 0; i < 2; i++)
-      {
-         best_results[i].m_n = 8;
-         best_results[i].m_pSelectors = best_selectors[i];
-      }
-      
-      uint8 selectors[3][8];
-      etc1_optimizer::results results[3];
-      
-      for (uint i = 0; i < 3; i++)
-      {
-         results[i].m_n = 8;
-         results[i].m_pSelectors = selectors[i];
-      }
-            
-      color_quad_u8 subblock_pixels[8];
-
-      etc1_optimizer::params params(pack_params);
-      params.m_num_src_pixels = 8;
-      params.m_pSrc_pixels = subblock_pixels;
-
-      for (uint flip = 0; flip < 2; flip++)
-      {
-         for (uint use_color4 = 0; use_color4 < 2; use_color4++)
-         {
-            uint64 trial_error = 0;
-
-            uint subblock;
-            for (subblock = 0; subblock < 2; subblock++)
-            {
-               if (flip)
-                  memcpy(subblock_pixels, pSrc_pixels + subblock * 8, sizeof(color_quad_u8) * 8);
-               else
-               {
-                  const color_quad_u8* pSrc_col = pSrc_pixels + subblock * 2;
-                  subblock_pixels[0] = pSrc_col[0]; subblock_pixels[1] = pSrc_col[4]; subblock_pixels[2] = pSrc_col[8]; subblock_pixels[3] = pSrc_col[12];
-                  subblock_pixels[4] = pSrc_col[1]; subblock_pixels[5] = pSrc_col[5]; subblock_pixels[6] = pSrc_col[9]; subblock_pixels[7] = pSrc_col[13];
-               }
-
-               results[2].m_error = cUINT64_MAX;
-               if ((params.m_quality >= cMediumQuality) && ((subblock) || (use_color4)))
-               {
-                  const uint32 subblock_pixel0_u32 = subblock_pixels[0].m_u32;
-                  for (r = 7; r >= 1; --r)
-                     if (subblock_pixels[r].m_u32 != subblock_pixel0_u32)
-                        break;
-                  if (!r)
-                  {
-                     pack_etc1_block_solid_color_constrained(results[2], 8, &subblock_pixels[0].r, pack_params, !use_color4, (subblock && !use_color4) ? &results[0].m_block_color_unscaled : NULL);
-                  }
-               }
-
-               params.m_use_color4 = (use_color4 != 0);
-               params.m_constrain_against_base_color5 = false;
-
-               if ((!use_color4) && (subblock))
-               {
-                  params.m_constrain_against_base_color5 = true;
-                  params.m_base_color5 = results[0].m_block_color_unscaled;
-               }
-                              
-               if (params.m_quality == cHighQuality)
-               {
-                  static const int s_scan_delta_0_to_4[] = { -4, -3, -2, -1, 0, 1, 2, 3, 4 };
-                  params.m_scan_delta_size = RG_ETC1_ARRAY_SIZE(s_scan_delta_0_to_4);
-                  params.m_pScan_deltas = s_scan_delta_0_to_4;
-               }
-               else if (params.m_quality == cMediumQuality)
-               {
-                  static const int s_scan_delta_0_to_1[] = { -1, 0, 1 };
-                  params.m_scan_delta_size = RG_ETC1_ARRAY_SIZE(s_scan_delta_0_to_1);
-                  params.m_pScan_deltas = s_scan_delta_0_to_1;
-               }
-               else
-               {
-                  static const int s_scan_delta_0[] = { 0 };
-                  params.m_scan_delta_size = RG_ETC1_ARRAY_SIZE(s_scan_delta_0);
-                  params.m_pScan_deltas = s_scan_delta_0;
-               }
-               
-               optimizer.init(params, results[subblock]);
-               if (!optimizer.compute())
-                  break;
-                              
-               if (params.m_quality >= cMediumQuality)
-               {
-                  // TODO: Fix fairly arbitrary/unrefined thresholds that control how far away to scan for potentially better solutions.
-                  const uint refinement_error_thresh0 = 3000;
-                  const uint refinement_error_thresh1 = 6000;
-                  if (results[subblock].m_error > refinement_error_thresh0)
-                  {
-                     if (params.m_quality == cMediumQuality)
-                     {
-                        static const int s_scan_delta_2_to_3[] = { -3, -2, 2, 3 };
-                        params.m_scan_delta_size = RG_ETC1_ARRAY_SIZE(s_scan_delta_2_to_3);
-                        params.m_pScan_deltas = s_scan_delta_2_to_3;
-                     }
-                     else
-                     {
-                        static const int s_scan_delta_5_to_5[] = { -5, 5 };
-                        static const int s_scan_delta_5_to_8[] = { -8, -7, -6, -5, 5, 6, 7, 8 };
-                        if (results[subblock].m_error > refinement_error_thresh1)
-                        {
-                           params.m_scan_delta_size = RG_ETC1_ARRAY_SIZE(s_scan_delta_5_to_8);
-                           params.m_pScan_deltas = s_scan_delta_5_to_8;
-                        }
-                        else
-                        {
-                           params.m_scan_delta_size = RG_ETC1_ARRAY_SIZE(s_scan_delta_5_to_5);
-                           params.m_pScan_deltas = s_scan_delta_5_to_5;
-                        }
-                     }
-
-                     if (!optimizer.compute())
-                        break;
-                  }
-
-                  if (results[2].m_error < results[subblock].m_error)
-                     results[subblock] = results[2];
-               }
-                            
-               trial_error += results[subblock].m_error;
-               if (trial_error >= best_error)
-                  break;
-            }
-
-            if (subblock < 2)
-               continue;
-
-            best_error = trial_error;
-            best_results[0] = results[0];
-            best_results[1] = results[1];
-            best_flip = flip;
-            best_use_color4 = use_color4;
-            
-         } // use_color4
-
-      } // flip
-
-      int dr = best_results[1].m_block_color_unscaled.r - best_results[0].m_block_color_unscaled.r;
-      int dg = best_results[1].m_block_color_unscaled.g - best_results[0].m_block_color_unscaled.g;
-      int db = best_results[1].m_block_color_unscaled.b - best_results[0].m_block_color_unscaled.b;
-      RG_ETC1_ASSERT(best_use_color4 || (rg_etc1::minimum(dr, dg, db) >= cETC1ColorDeltaMin) && (rg_etc1::maximum(dr, dg, db) <= cETC1ColorDeltaMax));
-           
-      if (best_use_color4)
-      {
-         dst_block.m_bytes[0] = static_cast<uint8>(best_results[1].m_block_color_unscaled.r | (best_results[0].m_block_color_unscaled.r << 4));
-         dst_block.m_bytes[1] = static_cast<uint8>(best_results[1].m_block_color_unscaled.g | (best_results[0].m_block_color_unscaled.g << 4));
-         dst_block.m_bytes[2] = static_cast<uint8>(best_results[1].m_block_color_unscaled.b | (best_results[0].m_block_color_unscaled.b << 4));
-      }
-      else
-      {
-         if (dr < 0) dr += 8; dst_block.m_bytes[0] = static_cast<uint8>((best_results[0].m_block_color_unscaled.r << 3) | dr);
-         if (dg < 0) dg += 8; dst_block.m_bytes[1] = static_cast<uint8>((best_results[0].m_block_color_unscaled.g << 3) | dg);
-         if (db < 0) db += 8; dst_block.m_bytes[2] = static_cast<uint8>((best_results[0].m_block_color_unscaled.b << 3) | db);
-      }
-      
-      dst_block.m_bytes[3] = static_cast<uint8>( (best_results[1].m_block_inten_table << 2) | (best_results[0].m_block_inten_table << 5) | ((~best_use_color4 & 1) << 1) | best_flip );
-      
-      uint selector0 = 0, selector1 = 0;
-      if (best_flip)
-      {
-         // flipped:
-         // { 0, 0 }, { 1, 0 }, { 2, 0 }, { 3, 0 },               
-         // { 0, 1 }, { 1, 1 }, { 2, 1 }, { 3, 1 } 
-         //
-         // { 0, 2 }, { 1, 2 }, { 2, 2 }, { 3, 2 },
-         // { 0, 3 }, { 1, 3 }, { 2, 3 }, { 3, 3 }
-         const uint8* pSelectors0 = best_results[0].m_pSelectors;
-         const uint8* pSelectors1 = best_results[1].m_pSelectors;
-         for (int x = 3; x >= 0; --x)
-         {
-            uint b;
-            b = g_selector_index_to_etc1[pSelectors1[4 + x]];
-            selector0 = (selector0 << 1) | (b & 1); selector1 = (selector1 << 1) | (b >> 1);
-
-            b = g_selector_index_to_etc1[pSelectors1[x]];
-            selector0 = (selector0 << 1) | (b & 1); selector1 = (selector1 << 1) | (b >> 1);
-
-            b = g_selector_index_to_etc1[pSelectors0[4 + x]];
-            selector0 = (selector0 << 1) | (b & 1); selector1 = (selector1 << 1) | (b >> 1);
-
-            b = g_selector_index_to_etc1[pSelectors0[x]];
-            selector0 = (selector0 << 1) | (b & 1); selector1 = (selector1 << 1) | (b >> 1);
-         }
-      }
-      else
-      {
-         // non-flipped:
-         // { 0, 0 }, { 0, 1 }, { 0, 2 }, { 0, 3 },
-         // { 1, 0 }, { 1, 1 }, { 1, 2 }, { 1, 3 }
-         //
-         // { 2, 0 }, { 2, 1 }, { 2, 2 }, { 2, 3 },
-         // { 3, 0 }, { 3, 1 }, { 3, 2 }, { 3, 3 }
-         for (int subblock = 1; subblock >= 0; --subblock)
-         {
-            const uint8* pSelectors = best_results[subblock].m_pSelectors + 4;
-            for (uint i = 0; i < 2; i++)
-            {
-               uint b;
-               b = g_selector_index_to_etc1[pSelectors[3]];
-               selector0 = (selector0 << 1) | (b & 1); selector1 = (selector1 << 1) | (b >> 1);
-
-               b = g_selector_index_to_etc1[pSelectors[2]];
-               selector0 = (selector0 << 1) | (b & 1); selector1 = (selector1 << 1) | (b >> 1);
-
-               b = g_selector_index_to_etc1[pSelectors[1]];
-               selector0 = (selector0 << 1) | (b & 1); selector1 = (selector1 << 1) | (b >> 1);
-
-               b = g_selector_index_to_etc1[pSelectors[0]];
-               selector0 = (selector0 << 1) | (b & 1);selector1 = (selector1 << 1) | (b >> 1);
-
-               pSelectors -= 4;
-            }
-         }
-      }
-                  
-      dst_block.m_bytes[4] = static_cast<uint8>(selector1 >> 8); dst_block.m_bytes[5] = static_cast<uint8>(selector1 & 0xFF);
-      dst_block.m_bytes[6] = static_cast<uint8>(selector0 >> 8); dst_block.m_bytes[7] = static_cast<uint8>(selector0 & 0xFF);
-
-      return static_cast<unsigned int>(best_error);
-   }
-
-} // namespace rg_etc1

+ 0 - 76
thirdparty/rg-etc1/rg_etc1.h

@@ -1,76 +0,0 @@
-// File: rg_etc1.h - Fast, high quality ETC1 block packer/unpacker - Rich Geldreich <[email protected]>
-// Please see ZLIB license at the end of this file.
-#pragma once
-
-namespace rg_etc1
-{
-   // Unpacks an 8-byte ETC1 compressed block to a block of 4x4 32bpp RGBA pixels.
-   // Returns false if the block is invalid. Invalid blocks will still be unpacked with clamping.
-   // This function is thread safe, and does not dynamically allocate any memory.
-   // If preserve_alpha is true, the alpha channel of the destination pixels will not be overwritten. Otherwise, alpha will be set to 255.
-   bool unpack_etc1_block(const void *pETC1_block, unsigned int* pDst_pixels_rgba, bool preserve_alpha = false);
-
-   // Quality setting = the higher the quality, the slower. 
-   // To pack large textures, it is highly recommended to call pack_etc1_block() in parallel, on different blocks, from multiple threads (particularly when using cHighQuality).
-   enum etc1_quality
-   { 
-      cLowQuality,
-      cMediumQuality,
-      cHighQuality,
-   };
-      
-   struct etc1_pack_params
-   {
-      etc1_quality m_quality;
-      bool m_dithering;
-                              
-      inline etc1_pack_params() 
-      {
-         clear();
-      }
-
-      void clear()
-      {
-         m_quality = cHighQuality;
-         m_dithering = false;
-      }
-   };
-
-   // Important: pack_etc1_block_init() must be called before calling pack_etc1_block().
-   void pack_etc1_block_init();
-
-   // Packs a 4x4 block of 32bpp RGBA pixels to an 8-byte ETC1 block.
-   // 32-bit RGBA pixels must always be arranged as (R,G,B,A) (R first, A last) in memory, independent of platform endianness. A should always be 255.
-   // Returns squared error of result.
-   // This function is thread safe, and does not dynamically allocate any memory.
-   // pack_etc1_block() does not currently support "perceptual" colorspace metrics - it primarily optimizes for RGB RMSE.
-   unsigned int pack_etc1_block(void* pETC1_block, const unsigned int* pSrc_pixels_rgba, etc1_pack_params& pack_params);
-            
-} // namespace rg_etc1
-
-//------------------------------------------------------------------------------
-//
-// rg_etc1 uses the ZLIB license:
-// http://opensource.org/licenses/Zlib
-//
-// Copyright (c) 2012 Rich Geldreich
-//
-// This software is provided 'as-is', without any express or implied
-// warranty.  In no event will the authors be held liable for any damages
-// arising from the use of this software.
-//
-// Permission is granted to anyone to use this software for any purpose,
-// including commercial applications, and to alter it and redistribute it
-// freely, subject to the following restrictions:
-//
-// 1. The origin of this software must not be misrepresented; you must not
-// claim that you wrote the original software. If you use this software
-// in a product, an acknowledgment in the product documentation would be
-// appreciated but is not required.
-//
-// 2. Altered source versions must be plainly marked as such, and must not be
-// misrepresented as being the original software.
-//
-// 3. This notice may not be removed or altered from any source distribution.
-//
-//------------------------------------------------------------------------------