diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9056de3389..36dbf12c73 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1099,6 +1099,28 @@ else()
include_directories(ext/libpng17)
endif()
+add_library(basis_universal STATIC
+ ext/basis_universal/basisu.h
+ ext/basis_universal/basisu_containers.h
+ ext/basis_universal/basisu_containers_impl.h
+ ext/basis_universal/basisu_file_headers.h
+ ext/basis_universal/basisu_transcoder.cpp
+ ext/basis_universal/basisu_transcoder.h
+ ext/basis_universal/basisu_transcoder_internal.h
+ ext/basis_universal/basisu_transcoder_tables_astc.inc
+ ext/basis_universal/basisu_transcoder_tables_astc_0_255.inc
+ ext/basis_universal/basisu_transcoder_tables_atc_55.inc
+ ext/basis_universal/basisu_transcoder_tables_atc_56.inc
+ ext/basis_universal/basisu_transcoder_tables_bc7_m5_alpha.inc
+ ext/basis_universal/basisu_transcoder_tables_bc7_m5_color.inc
+ ext/basis_universal/basisu_transcoder_tables_dxt1_5.inc
+ ext/basis_universal/basisu_transcoder_tables_dxt1_6.inc
+ ext/basis_universal/basisu_transcoder_tables_pvrtc2_45.inc
+ ext/basis_universal/basisu_transcoder_tables_pvrtc2_alpha_33.inc
+ ext/basis_universal/basisu_transcoder_uastc.h
+)
+set(BASISU_LIBRARIES basis_universal)
+
set(nativeExtra)
set(nativeExtraLibs)
@@ -1362,7 +1384,7 @@ if(ANDROID OR (LINUX AND ARM_DEVICE) OR (LINUX AND RISCV64))
set(ATOMIC_LIB atomic)
endif()
-target_link_libraries(native ${LIBZIP_LIBRARY} ${PNG_LIBRARIES} ${ZLIB_LIBRARY} vma gason udis86 ${RT_LIB} ${nativeExtraLibs} ${ATOMIC_LIB} Common)
+target_link_libraries(native ${LIBZIP_LIBRARY} ${PNG_LIBRARIES} ${BASISU_LIBRARIES} ${ZLIB_LIBRARY} vma gason udis86 ${RT_LIB} ${nativeExtraLibs} ${ATOMIC_LIB} Common)
if(TARGET Ext::GLEW)
target_link_libraries(native Ext::GLEW)
endif()
diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj
index 90d8d0d872..c066494c66 100644
--- a/Common/Common.vcxproj
+++ b/Common/Common.vcxproj
@@ -377,6 +377,13 @@
+
+
+
+
+
+
+
@@ -577,6 +584,7 @@
+
NotUsing
NotUsing
@@ -1034,6 +1042,18 @@
{f761046e-6c38-4428-a5f1-38391a37bb34}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters
index 053702cd0a..cce4e7656a 100644
--- a/Common/Common.vcxproj.filters
+++ b/Common/Common.vcxproj.filters
@@ -473,6 +473,27 @@
Data\Format
+
+ ext\basis_universal
+
+
+ ext\basis_universal
+
+
+ ext\basis_universal
+
+
+ ext\basis_universal
+
+
+ ext\basis_universal
+
+
+ ext\basis_universal
+
+
+ ext\basis_universal
+
@@ -896,6 +917,9 @@
Data\Format
+
+ ext\basis_universal
+
@@ -1003,10 +1027,45 @@
{9d1c29fd-8ac7-4475-8ea6-c8c759b695fe}
+
+ {d6d5f6e0-1c72-496b-af11-6d52d5123033}
+
ext\libpng17
+
+
+ ext\basis_universal
+
+
+ ext\basis_universal
+
+
+ ext\basis_universal
+
+
+ ext\basis_universal
+
+
+ ext\basis_universal
+
+
+ ext\basis_universal
+
+
+ ext\basis_universal
+
+
+ ext\basis_universal
+
+
+ ext\basis_universal
+
+
+ ext\basis_universal
+
+
\ No newline at end of file
diff --git a/GPU/Common/ReplacedTexture.cpp b/GPU/Common/ReplacedTexture.cpp
index 3fe5579175..f2497ae9b5 100644
--- a/GPU/Common/ReplacedTexture.cpp
+++ b/GPU/Common/ReplacedTexture.cpp
@@ -20,6 +20,8 @@
#include
+#include "ext/basis_universal/basisu_transcoder.h"
+
#include "GPU/Common/ReplacedTexture.h"
#include "GPU/Common/TextureReplacer.h"
diff --git a/android/jni/Android.mk b/android/jni/Android.mk
index 6ff34b212f..61aaefc181 100644
--- a/android/jni/Android.mk
+++ b/android/jni/Android.mk
@@ -103,6 +103,7 @@ EXT_FILES := \
$(SRC)/ext/libpng17/pngwrite.c \
$(SRC)/ext/libpng17/pngwtran.c \
$(SRC)/ext/libpng17/pngwutil.c \
+ $(SRC)/ext/basis_universal/basisu_transcoder.cpp \
$(SRC)/ext/jpge/jpgd.cpp \
$(SRC)/ext/jpge/jpge.cpp \
$(SRC)/ext/sha1/sha1.cpp \
diff --git a/ext/basis_universal/basisu.h b/ext/basis_universal/basisu.h
new file mode 100644
index 0000000000..1230b59ec6
--- /dev/null
+++ b/ext/basis_universal/basisu.h
@@ -0,0 +1,478 @@
+// basisu.h
+// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
+// Important: If compiling with gcc, be sure strict aliasing is disabled: -fno-strict-aliasing
+//
+// 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 _MSC_VER
+
+ #pragma warning (disable : 4201)
+ #pragma warning (disable : 4127) // warning C4127: conditional expression is constant
+ #pragma warning (disable : 4530) // C++ exception handler used, but unwind semantics are not enabled.
+
+ // Slamming this off always for v1.16 because we've gotten rid of most std containers.
+ #ifndef BASISU_NO_ITERATOR_DEBUG_LEVEL
+ #define BASISU_NO_ITERATOR_DEBUG_LEVEL (1)
+ #endif
+
+ #ifndef BASISU_NO_ITERATOR_DEBUG_LEVEL
+ //#define _HAS_ITERATOR_DEBUGGING 0
+
+ #if defined(_DEBUG) || defined(DEBUG)
+ // This is madness, but we need to disable iterator debugging in debug builds or the encoder is unsable because MSVC's iterator debugging implementation is totally broken.
+ #ifndef _ITERATOR_DEBUG_LEVEL
+ #define _ITERATOR_DEBUG_LEVEL 1
+ #endif
+ #ifndef _SECURE_SCL
+ #define _SECURE_SCL 1
+ #endif
+ #else // defined(_DEBUG) || defined(DEBUG)
+ #ifndef _SECURE_SCL
+ #define _SECURE_SCL 0
+ #endif
+ #ifndef _ITERATOR_DEBUG_LEVEL
+ #define _ITERATOR_DEBUG_LEVEL 0
+ #endif
+ #endif // defined(_DEBUG) || defined(DEBUG)
+
+ #endif // BASISU_NO_ITERATOR_DEBUG_LEVEL
+
+#endif // _MSC_VER
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "basisu_containers.h"
+
+#ifdef max
+#undef max
+#endif
+
+#ifdef min
+#undef min
+#endif
+
+#ifdef _WIN32
+#define strcasecmp _stricmp
+#endif
+
+// Set to one to enable debug printf()'s when any errors occur, for development/debugging. Especially useful for WebGL development.
+#ifndef BASISU_FORCE_DEVEL_MESSAGES
+#define BASISU_FORCE_DEVEL_MESSAGES 0
+#endif
+
+#define BASISU_NOTE_UNUSED(x) (void)(x)
+#define BASISU_ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
+#define BASISU_NO_EQUALS_OR_COPY_CONSTRUCT(x) x(const x &) = delete; x& operator= (const x &) = delete;
+#define BASISU_ASSUME(x) static_assert(x, #x);
+#define BASISU_OFFSETOF(s, m) offsetof(s, m)
+#define BASISU_STRINGIZE(x) #x
+#define BASISU_STRINGIZE2(x) BASISU_STRINGIZE(x)
+
+#if BASISU_FORCE_DEVEL_MESSAGES
+ #define BASISU_DEVEL_ERROR(...) do { basisu::debug_printf(__VA_ARGS__); } while(0)
+#else
+ #define BASISU_DEVEL_ERROR(...)
+#endif
+
+namespace basisu
+{
+ // Types/utilities
+
+#ifdef _WIN32
+ const char BASISU_PATH_SEPERATOR_CHAR = '\\';
+#else
+ const char BASISU_PATH_SEPERATOR_CHAR = '/';
+#endif
+
+ typedef basisu::vector uint8_vec;
+ typedef basisu::vector int16_vec;
+ typedef basisu::vector uint16_vec;
+ typedef basisu::vector uint_vec;
+ typedef basisu::vector uint64_vec;
+ typedef basisu::vector int_vec;
+ typedef basisu::vector bool_vec;
+
+ void enable_debug_printf(bool enabled);
+ void debug_printf(const char *pFmt, ...);
+
+
+ template inline void clear_obj(T& obj) { memset(&obj, 0, sizeof(obj)); }
+
+ template inline T0 lerp(T0 a, T0 b, T1 c) { return a + (b - a) * c; }
+
+ template inline S maximum(S a, S b) { return (a > b) ? a : b; }
+ template inline S maximum(S a, S b, S c) { return maximum(maximum(a, b), c); }
+ template inline S maximum(S a, S b, S c, S d) { return maximum(maximum(maximum(a, b), c), d); }
+
+ template inline S minimum(S a, S b) { return (a < b) ? a : b; }
+ template inline S minimum(S a, S b, S c) { return minimum(minimum(a, b), c); }
+ template inline S minimum(S a, S b, S c, S d) { return minimum(minimum(minimum(a, b), c), d); }
+
+ inline float clampf(float value, float low, float high) { if (value < low) value = low; else if (value > high) value = high; return value; }
+ inline float saturate(float value) { return clampf(value, 0, 1.0f); }
+ inline uint8_t minimumub(uint8_t a, uint8_t b) { return (a < b) ? a : b; }
+ inline uint32_t minimumu(uint32_t a, uint32_t b) { return (a < b) ? a : b; }
+ inline int32_t minimumi(int32_t a, int32_t b) { return (a < b) ? a : b; }
+ inline float minimumf(float a, float b) { return (a < b) ? a : b; }
+ inline uint8_t maximumub(uint8_t a, uint8_t b) { return (a > b) ? a : b; }
+ inline uint32_t maximumu(uint32_t a, uint32_t b) { return (a > b) ? a : b; }
+ inline int32_t maximumi(int32_t a, int32_t b) { return (a > b) ? a : b; }
+ inline float maximumf(float a, float b) { return (a > b) ? a : b; }
+ inline int squarei(int i) { return i * i; }
+ inline float squaref(float i) { return i * i; }
+ template inline T square(T a) { return a * a; }
+
+ template inline S clamp(S value, S low, S high) { return (value < low) ? low : ((value > high) ? high : value); }
+
+ inline uint32_t iabs(int32_t i) { return (i < 0) ? static_cast(-i) : static_cast(i); }
+ inline uint64_t iabs64(int64_t i) { return (i < 0) ? static_cast(-i) : static_cast(i); }
+
+ template inline void clear_vector(T &vec) { vec.erase(vec.begin(), vec.end()); }
+ template inline typename T::value_type *enlarge_vector(T &vec, size_t n) { size_t cs = vec.size(); vec.resize(cs + n); return &vec[cs]; }
+
+ inline bool is_pow2(uint32_t x) { return x && ((x & (x - 1U)) == 0U); }
+ inline bool is_pow2(uint64_t x) { return x && ((x & (x - 1U)) == 0U); }
+
+ template inline T open_range_check(T v, T minv, T maxv) { assert(v >= minv && v < maxv); BASISU_NOTE_UNUSED(minv); BASISU_NOTE_UNUSED(maxv); return v; }
+ template inline T open_range_check(T v, T maxv) { assert(v < maxv); BASISU_NOTE_UNUSED(maxv); return v; }
+
+ inline uint32_t total_bits(uint32_t v) { uint32_t l = 0; for ( ; v > 0U; ++l) v >>= 1; return l; }
+
+ template inline T saturate(T val) { return clamp(val, 0.0f, 1.0f); }
+
+ template inline void append_vector(T &vec, const R *pObjs, size_t n)
+ {
+ if (n)
+ {
+ if (vec.size())
+ {
+ assert((pObjs + n) <= vec.begin() || (pObjs >= vec.end()));
+ }
+ const size_t cur_s = vec.size();
+ vec.resize(cur_s + n);
+ memcpy(&vec[cur_s], pObjs, sizeof(R) * n);
+ }
+ }
+
+ template inline void append_vector(T &vec, const T &other_vec)
+ {
+ assert(&vec != &other_vec);
+ if (other_vec.size())
+ append_vector(vec, &other_vec[0], other_vec.size());
+ }
+
+ template inline void vector_ensure_element_is_valid(T &vec, size_t idx)
+ {
+ if (idx >= vec.size())
+ vec.resize(idx + 1);
+ }
+
+ template inline void vector_sort(T &vec)
+ {
+ if (vec.size())
+ std::sort(vec.begin(), vec.end());
+ }
+
+ template inline bool unordered_set_contains(T& set, const U&obj)
+ {
+ return set.find(obj) != set.end();
+ }
+
+ template int vector_find(const T &vec, const typename T::value_type &obj)
+ {
+ assert(vec.size() <= INT_MAX);
+ for (size_t i = 0; i < vec.size(); i++)
+ if (vec[i] == obj)
+ return static_cast(i);
+ return -1;
+ }
+
+ template void vector_set_all(T &vec, const typename T::value_type &obj)
+ {
+ for (size_t i = 0; i < vec.size(); i++)
+ vec[i] = obj;
+ }
+
+ inline uint64_t read_be64(const void *p)
+ {
+ uint64_t val = 0;
+ for (uint32_t i = 0; i < 8; i++)
+ val |= (static_cast(static_cast(p)[7 - i]) << (i * 8));
+ return val;
+ }
+
+ inline void write_be64(void *p, uint64_t x)
+ {
+ for (uint32_t i = 0; i < 8; i++)
+ static_cast(p)[7 - i] = static_cast(x >> (i * 8));
+ }
+
+ static inline uint16_t byteswap16(uint16_t x) { return static_cast((x << 8) | (x >> 8)); }
+ static inline uint32_t byteswap32(uint32_t x) { return ((x << 24) | ((x << 8) & 0x00FF0000) | ((x >> 8) & 0x0000FF00) | (x >> 24)); }
+
+ inline uint32_t floor_log2i(uint32_t v)
+ {
+ uint32_t b = 0;
+ for (; v > 1U; ++b)
+ v >>= 1;
+ return b;
+ }
+
+ inline uint32_t ceil_log2i(uint32_t v)
+ {
+ uint32_t b = floor_log2i(v);
+ if ((b != 32) && (v > (1U << b)))
+ ++b;
+ return b;
+ }
+
+ inline int posmod(int x, int y)
+ {
+ if (x >= 0)
+ return (x < y) ? x : (x % y);
+ int m = (-x) % y;
+ return (m != 0) ? (y - m) : m;
+ }
+
+ inline bool do_excl_ranges_overlap(int la, int ha, int lb, int hb)
+ {
+ assert(la < ha && lb < hb);
+ if ((ha <= lb) || (la >= hb)) return false;
+ return true;
+ }
+
+ static inline uint32_t read_le_dword(const uint8_t *pBytes)
+ {
+ return (pBytes[3] << 24U) | (pBytes[2] << 16U) | (pBytes[1] << 8U) | (pBytes[0]);
+ }
+
+ static inline void write_le_dword(uint8_t* pBytes, uint32_t val)
+ {
+ pBytes[0] = (uint8_t)val;
+ pBytes[1] = (uint8_t)(val >> 8U);
+ pBytes[2] = (uint8_t)(val >> 16U);
+ pBytes[3] = (uint8_t)(val >> 24U);
+ }
+
+ // Always little endian 1-8 byte unsigned int
+ template
+ struct packed_uint
+ {
+ uint8_t m_bytes[NumBytes];
+
+ inline packed_uint() { static_assert(NumBytes <= sizeof(uint64_t), "Invalid NumBytes"); }
+ inline packed_uint(uint64_t v) { *this = v; }
+ inline packed_uint(const packed_uint& other) { *this = other; }
+
+ inline packed_uint& operator= (uint64_t v)
+ {
+ for (uint32_t i = 0; i < NumBytes; i++)
+ m_bytes[i] = static_cast(v >> (i * 8));
+ return *this;
+ }
+
+ inline packed_uint& operator= (const packed_uint& rhs)
+ {
+ memcpy(m_bytes, rhs.m_bytes, sizeof(m_bytes));
+ return *this;
+ }
+
+ inline operator uint32_t() const
+ {
+ switch (NumBytes)
+ {
+ case 1:
+ {
+ return m_bytes[0];
+ }
+ case 2:
+ {
+ return (m_bytes[1] << 8U) | m_bytes[0];
+ }
+ case 3:
+ {
+ return (m_bytes[2] << 16U) | (m_bytes[1] << 8U) | m_bytes[0];
+ }
+ case 4:
+ {
+ return read_le_dword(m_bytes);
+ }
+ case 5:
+ {
+ uint32_t l = read_le_dword(m_bytes);
+ uint32_t h = m_bytes[4];
+ return static_cast(l) | (static_cast(h) << 32U);
+ }
+ case 6:
+ {
+ uint32_t l = read_le_dword(m_bytes);
+ uint32_t h = (m_bytes[5] << 8U) | m_bytes[4];
+ return static_cast(l) | (static_cast(h) << 32U);
+ }
+ case 7:
+ {
+ uint32_t l = read_le_dword(m_bytes);
+ uint32_t h = (m_bytes[6] << 16U) | (m_bytes[5] << 8U) | m_bytes[4];
+ return static_cast(l) | (static_cast(h) << 32U);
+ }
+ case 8:
+ {
+ uint32_t l = read_le_dword(m_bytes);
+ uint32_t h = read_le_dword(m_bytes + 4);
+ return static_cast(l) | (static_cast(h) << 32U);
+ }
+ default:
+ {
+ assert(0);
+ return 0;
+ }
+ }
+ }
+ };
+
+ enum eZero { cZero };
+ enum eNoClamp { cNoClamp };
+
+ // Rice/Huffman entropy coding
+
+ // This is basically Deflate-style canonical Huffman, except we allow for a lot more symbols.
+ enum
+ {
+ cHuffmanMaxSupportedCodeSize = 16, cHuffmanMaxSupportedInternalCodeSize = 31,
+ cHuffmanFastLookupBits = 10,
+ cHuffmanMaxSymsLog2 = 14, cHuffmanMaxSyms = 1 << cHuffmanMaxSymsLog2,
+
+ // Small zero runs
+ cHuffmanSmallZeroRunSizeMin = 3, cHuffmanSmallZeroRunSizeMax = 10, cHuffmanSmallZeroRunExtraBits = 3,
+
+ // Big zero run
+ cHuffmanBigZeroRunSizeMin = 11, cHuffmanBigZeroRunSizeMax = 138, cHuffmanBigZeroRunExtraBits = 7,
+
+ // Small non-zero run
+ cHuffmanSmallRepeatSizeMin = 3, cHuffmanSmallRepeatSizeMax = 6, cHuffmanSmallRepeatExtraBits = 2,
+
+ // Big non-zero run
+ cHuffmanBigRepeatSizeMin = 7, cHuffmanBigRepeatSizeMax = 134, cHuffmanBigRepeatExtraBits = 7,
+
+ cHuffmanTotalCodelengthCodes = 21, cHuffmanSmallZeroRunCode = 17, cHuffmanBigZeroRunCode = 18, cHuffmanSmallRepeatCode = 19, cHuffmanBigRepeatCode = 20
+ };
+
+ static const uint8_t g_huffman_sorted_codelength_codes[] = { cHuffmanSmallZeroRunCode, cHuffmanBigZeroRunCode, cHuffmanSmallRepeatCode, cHuffmanBigRepeatCode, 0, 8, 7, 9, 6, 0xA, 5, 0xB, 4, 0xC, 3, 0xD, 2, 0xE, 1, 0xF, 0x10 };
+ const uint32_t cHuffmanTotalSortedCodelengthCodes = sizeof(g_huffman_sorted_codelength_codes) / sizeof(g_huffman_sorted_codelength_codes[0]);
+
+ // GPU texture formats
+
+ enum class texture_format
+ {
+ cInvalidTextureFormat = -1,
+
+ // Block-based formats
+ cETC1, // ETC1
+ cETC1S, // ETC1 (subset: diff colors only, no subblocks)
+ cETC2_RGB, // ETC2 color block (basisu doesn't support ETC2 planar/T/H modes - just basic ETC1)
+ cETC2_RGBA, // ETC2 EAC alpha block followed by ETC2 color block
+ cETC2_ALPHA, // ETC2 EAC alpha block
+ cBC1, // DXT1
+ cBC3, // DXT5 (BC4/DXT5A block followed by a BC1/DXT1 block)
+ cBC4, // DXT5A
+ cBC5, // 3DC/DXN (two BC4/DXT5A blocks)
+ cBC7,
+ cASTC4x4, // LDR only
+ cPVRTC1_4_RGB,
+ cPVRTC1_4_RGBA,
+ cATC_RGB,
+ cATC_RGBA_INTERPOLATED_ALPHA,
+ cFXT1_RGB,
+ cPVRTC2_4_RGBA,
+ cETC2_R11_EAC,
+ cETC2_RG11_EAC,
+ cUASTC4x4,
+ cBC1_NV,
+ cBC1_AMD,
+
+ // Uncompressed/raw pixels
+ cRGBA32,
+ cRGB565,
+ cBGR565,
+ cRGBA4444,
+ cABGR4444
+ };
+
+ inline uint32_t get_bytes_per_block(texture_format fmt)
+ {
+ switch (fmt)
+ {
+ case texture_format::cETC1:
+ case texture_format::cETC1S:
+ case texture_format::cETC2_RGB:
+ case texture_format::cETC2_ALPHA:
+ case texture_format::cBC1:
+ case texture_format::cBC1_NV:
+ case texture_format::cBC1_AMD:
+ case texture_format::cBC4:
+ case texture_format::cPVRTC1_4_RGB:
+ case texture_format::cPVRTC1_4_RGBA:
+ case texture_format::cATC_RGB:
+ case texture_format::cPVRTC2_4_RGBA:
+ case texture_format::cETC2_R11_EAC:
+ return 8;
+ case texture_format::cRGBA32:
+ return sizeof(uint32_t) * 16;
+ default:
+ break;
+ }
+ return 16;
+ }
+
+ inline uint32_t get_qwords_per_block(texture_format fmt)
+ {
+ return get_bytes_per_block(fmt) >> 3;
+ }
+
+ inline uint32_t get_block_width(texture_format fmt)
+ {
+ BASISU_NOTE_UNUSED(fmt);
+ switch (fmt)
+ {
+ case texture_format::cFXT1_RGB:
+ return 8;
+ default:
+ break;
+ }
+ return 4;
+ }
+
+ inline uint32_t get_block_height(texture_format fmt)
+ {
+ BASISU_NOTE_UNUSED(fmt);
+ return 4;
+ }
+
+} // namespace basisu
+
diff --git a/ext/basis_universal/basisu_containers.h b/ext/basis_universal/basisu_containers.h
new file mode 100644
index 0000000000..d123ce4788
--- /dev/null
+++ b/ext/basis_universal/basisu_containers.h
@@ -0,0 +1,1986 @@
+// basisu_containers.h
+#pragma once
+
+#undef new
+
+#include
+#include
+#include
+#include
+#include
+
+#if defined(__linux__) && !defined(ANDROID)
+// Only for malloc_usable_size() in basisu_containers_impl.h
+#include
+#define HAS_MALLOC_USABLE_SIZE 1
+#endif
+
+// Set to 1 to always check vector operator[], front(), and back() even in release.
+#define BASISU_VECTOR_FORCE_CHECKING 0
+
+// If 1, the vector container will not query the CRT to get the size of resized memory blocks.
+#define BASISU_VECTOR_DETERMINISTIC 1
+
+#ifdef _MSC_VER
+#define BASISU_FORCE_INLINE __forceinline
+#else
+#define BASISU_FORCE_INLINE inline
+#endif
+
+namespace basisu
+{
+ enum { cInvalidIndex = -1 };
+
+ namespace helpers
+ {
+ inline bool is_power_of_2(uint32_t x) { return x && ((x & (x - 1U)) == 0U); }
+ inline bool is_power_of_2(uint64_t x) { return x && ((x & (x - 1U)) == 0U); }
+ template const T& minimum(const T& a, const T& b) { return (b < a) ? b : a; }
+ template const T& maximum(const T& a, const T& b) { return (a < b) ? b : a; }
+
+ inline uint32_t floor_log2i(uint32_t v)
+ {
+ uint32_t l = 0;
+ while (v > 1U)
+ {
+ v >>= 1;
+ l++;
+ }
+ return l;
+ }
+
+ inline uint32_t next_pow2(uint32_t val)
+ {
+ val--;
+ val |= val >> 16;
+ val |= val >> 8;
+ val |= val >> 4;
+ val |= val >> 2;
+ val |= val >> 1;
+ return val + 1;
+ }
+
+ inline uint64_t next_pow2(uint64_t val)
+ {
+ val--;
+ val |= val >> 32;
+ val |= val >> 16;
+ val |= val >> 8;
+ val |= val >> 4;
+ val |= val >> 2;
+ val |= val >> 1;
+ return val + 1;
+ }
+ } // namespace helpers
+
+ template
+ inline T* construct(T* p)
+ {
+ return new (static_cast(p)) T;
+ }
+
+ template
+ inline T* construct(T* p, const U& init)
+ {
+ return new (static_cast(p)) T(init);
+ }
+
+ template
+ inline void construct_array(T* p, size_t n)
+ {
+ T* q = p + n;
+ for (; p != q; ++p)
+ new (static_cast(p)) T;
+ }
+
+ template
+ inline void construct_array(T* p, size_t n, const U& init)
+ {
+ T* q = p + n;
+ for (; p != q; ++p)
+ new (static_cast(p)) T(init);
+ }
+
+ template
+ inline void destruct(T* p)
+ {
+ (void)p;
+ p->~T();
+ }
+
+ template inline void destruct_array(T* p, size_t n)
+ {
+ T* q = p + n;
+ for (; p != q; ++p)
+ p->~T();
+ }
+
+ template struct int_traits { enum { cMin = INT32_MIN, cMax = INT32_MAX, cSigned = true }; };
+
+ template<> struct int_traits { enum { cMin = INT8_MIN, cMax = INT8_MAX, cSigned = true }; };
+ template<> struct int_traits { enum { cMin = INT16_MIN, cMax = INT16_MAX, cSigned = true }; };
+ template<> struct int_traits { enum { cMin = INT32_MIN, cMax = INT32_MAX, cSigned = true }; };
+
+ template<> struct int_traits { enum { cMin = 0, cMax = UINT8_MAX, cSigned = false }; };
+ template<> struct int_traits { enum { cMin = 0, cMax = UINT16_MAX, cSigned = false }; };
+ template<> struct int_traits { enum { cMin = 0, cMax = UINT32_MAX, cSigned = false }; };
+
+ template
+ struct scalar_type
+ {
+ enum { cFlag = false };
+ static inline void construct(T* p) { basisu::construct(p); }
+ static inline void construct(T* p, const T& init) { basisu::construct(p, init); }
+ static inline void construct_array(T* p, size_t n) { basisu::construct_array(p, n); }
+ static inline void destruct(T* p) { basisu::destruct(p); }
+ static inline void destruct_array(T* p, size_t n) { basisu::destruct_array(p, n); }
+ };
+
+ template struct scalar_type
+ {
+ enum { cFlag = true };
+ static inline void construct(T** p) { memset(p, 0, sizeof(T*)); }
+ static inline void construct(T** p, T* init) { *p = init; }
+ static inline void construct_array(T** p, size_t n) { memset(p, 0, sizeof(T*) * n); }
+ static inline void destruct(T** p) { p; }
+ static inline void destruct_array(T** p, size_t n) { p, n; }
+ };
+
+#define BASISU_DEFINE_BUILT_IN_TYPE(X) \
+ template<> struct scalar_type { \
+ enum { cFlag = true }; \
+ static inline void construct(X* p) { memset(p, 0, sizeof(X)); } \
+ static inline void construct(X* p, const X& init) { memcpy(p, &init, sizeof(X)); } \
+ static inline void construct_array(X* p, size_t n) { memset(p, 0, sizeof(X) * n); } \
+ static inline void destruct(X* p) { p; } \
+ static inline void destruct_array(X* p, size_t n) { p, n; } };
+
+ BASISU_DEFINE_BUILT_IN_TYPE(bool)
+ BASISU_DEFINE_BUILT_IN_TYPE(char)
+ BASISU_DEFINE_BUILT_IN_TYPE(unsigned char)
+ BASISU_DEFINE_BUILT_IN_TYPE(short)
+ BASISU_DEFINE_BUILT_IN_TYPE(unsigned short)
+ BASISU_DEFINE_BUILT_IN_TYPE(int)
+ BASISU_DEFINE_BUILT_IN_TYPE(unsigned int)
+ BASISU_DEFINE_BUILT_IN_TYPE(long)
+ BASISU_DEFINE_BUILT_IN_TYPE(unsigned long)
+#ifdef __GNUC__
+ BASISU_DEFINE_BUILT_IN_TYPE(long long)
+ BASISU_DEFINE_BUILT_IN_TYPE(unsigned long long)
+#else
+ BASISU_DEFINE_BUILT_IN_TYPE(__int64)
+ BASISU_DEFINE_BUILT_IN_TYPE(unsigned __int64)
+#endif
+ BASISU_DEFINE_BUILT_IN_TYPE(float)
+ BASISU_DEFINE_BUILT_IN_TYPE(double)
+ BASISU_DEFINE_BUILT_IN_TYPE(long double)
+
+#undef BASISU_DEFINE_BUILT_IN_TYPE
+
+ template
+ struct bitwise_movable { enum { cFlag = false }; };
+
+#define BASISU_DEFINE_BITWISE_MOVABLE(Q) template<> struct bitwise_movable { enum { cFlag = true }; };
+
+ template
+ struct bitwise_copyable { enum { cFlag = false }; };
+
+#define BASISU_DEFINE_BITWISE_COPYABLE(Q) template<> struct bitwise_copyable { enum { cFlag = true }; };
+
+#define BASISU_IS_POD(T) __is_pod(T)
+
+#define BASISU_IS_SCALAR_TYPE(T) (scalar_type::cFlag)
+
+#if defined(__GNUC__) && __GNUC__<5
+ #define BASISU_IS_TRIVIALLY_COPYABLE(...) __has_trivial_copy(__VA_ARGS__)
+#else
+ #define BASISU_IS_TRIVIALLY_COPYABLE(...) std::is_trivially_copyable<__VA_ARGS__>::value
+#endif
+
+// TODO: clean this up
+#define BASISU_IS_BITWISE_COPYABLE(T) (BASISU_IS_SCALAR_TYPE(T) || BASISU_IS_POD(T) || BASISU_IS_TRIVIALLY_COPYABLE(T) || (bitwise_copyable::cFlag))
+
+#define BASISU_IS_BITWISE_COPYABLE_OR_MOVABLE(T) (BASISU_IS_BITWISE_COPYABLE(T) || (bitwise_movable::cFlag))
+
+#define BASISU_HAS_DESTRUCTOR(T) ((!scalar_type::cFlag) && (!__is_pod(T)))
+
+ typedef char(&yes_t)[1];
+ typedef char(&no_t)[2];
+
+ template yes_t class_test(int U::*);
+ template no_t class_test(...);
+
+ template struct is_class
+ {
+ enum { value = (sizeof(class_test(0)) == sizeof(yes_t)) };
+ };
+
+ template struct is_pointer
+ {
+ enum { value = false };
+ };
+
+ template struct is_pointer
+ {
+ enum { value = true };
+ };
+
+ struct empty_type { };
+
+ BASISU_DEFINE_BITWISE_COPYABLE(empty_type);
+ BASISU_DEFINE_BITWISE_MOVABLE(empty_type);
+
+ template struct rel_ops
+ {
+ friend bool operator!=(const T& x, const T& y) { return (!(x == y)); }
+ friend bool operator> (const T& x, const T& y) { return (y < x); }
+ friend bool operator<=(const T& x, const T& y) { return (!(y < x)); }
+ friend bool operator>=(const T& x, const T& y) { return (!(x < y)); }
+ };
+
+ struct elemental_vector
+ {
+ void* m_p;
+ uint32_t m_size;
+ uint32_t m_capacity;
+
+ typedef void (*object_mover)(void* pDst, void* pSrc, uint32_t num);
+
+ bool increase_capacity(uint32_t min_new_capacity, bool grow_hint, uint32_t element_size, object_mover pRelocate, bool nofail);
+ };
+
+ template
+ class vector : public rel_ops< vector >
+ {
+ public:
+ typedef T* iterator;
+ typedef const T* const_iterator;
+ typedef T value_type;
+ typedef T& reference;
+ typedef const T& const_reference;
+ typedef T* pointer;
+ typedef const T* const_pointer;
+
+ inline vector() :
+ m_p(NULL),
+ m_size(0),
+ m_capacity(0)
+ {
+ }
+
+ inline vector(uint32_t n, const T& init) :
+ m_p(NULL),
+ m_size(0),
+ m_capacity(0)
+ {
+ increase_capacity(n, false);
+ construct_array(m_p, n, init);
+ m_size = n;
+ }
+
+ inline vector(const vector& other) :
+ m_p(NULL),
+ m_size(0),
+ m_capacity(0)
+ {
+ increase_capacity(other.m_size, false);
+
+ m_size = other.m_size;
+
+ if (BASISU_IS_BITWISE_COPYABLE(T))
+ {
+ if ((m_p) && (other.m_p))
+ memcpy(m_p, other.m_p, m_size * sizeof(T));
+ }
+ else
+ {
+ T* pDst = m_p;
+ const T* pSrc = other.m_p;
+ for (uint32_t i = m_size; i > 0; i--)
+ construct(pDst++, *pSrc++);
+ }
+ }
+
+ inline explicit vector(size_t size) :
+ m_p(NULL),
+ m_size(0),
+ m_capacity(0)
+ {
+ resize(size);
+ }
+
+ inline ~vector()
+ {
+ if (m_p)
+ {
+ scalar_type::destruct_array(m_p, m_size);
+ free(m_p);
+ }
+ }
+
+ inline vector& operator= (const vector& other)
+ {
+ if (this == &other)
+ return *this;
+
+ if (m_capacity >= other.m_size)
+ resize(0);
+ else
+ {
+ clear();
+ increase_capacity(other.m_size, false);
+ }
+
+ if (BASISU_IS_BITWISE_COPYABLE(T))
+ {
+ if ((m_p) && (other.m_p))
+ memcpy(m_p, other.m_p, other.m_size * sizeof(T));
+ }
+ else
+ {
+ T* pDst = m_p;
+ const T* pSrc = other.m_p;
+ for (uint32_t i = other.m_size; i > 0; i--)
+ construct(pDst++, *pSrc++);
+ }
+
+ m_size = other.m_size;
+
+ return *this;
+ }
+
+ BASISU_FORCE_INLINE const T* begin() const { return m_p; }
+ BASISU_FORCE_INLINE T* begin() { return m_p; }
+
+ BASISU_FORCE_INLINE const T* end() const { return m_p + m_size; }
+ BASISU_FORCE_INLINE T* end() { return m_p + m_size; }
+
+ BASISU_FORCE_INLINE bool empty() const { return !m_size; }
+ BASISU_FORCE_INLINE uint32_t size() const { return m_size; }
+ BASISU_FORCE_INLINE uint32_t size_in_bytes() const { return m_size * sizeof(T); }
+ BASISU_FORCE_INLINE uint32_t capacity() const { return m_capacity; }
+
+ // operator[] will assert on out of range indices, but in final builds there is (and will never be) any range checking on this method.
+ //BASISU_FORCE_INLINE const T& operator[] (uint32_t i) const { assert(i < m_size); return m_p[i]; }
+ //BASISU_FORCE_INLINE T& operator[] (uint32_t i) { assert(i < m_size); return m_p[i]; }
+
+#if !BASISU_VECTOR_FORCE_CHECKING
+ BASISU_FORCE_INLINE const T& operator[] (size_t i) const { assert(i < m_size); return m_p[i]; }
+ BASISU_FORCE_INLINE T& operator[] (size_t i) { assert(i < m_size); return m_p[i]; }
+#else
+ BASISU_FORCE_INLINE const T& operator[] (size_t i) const
+ {
+ if (i >= m_size)
+ {
+ fprintf(stderr, "operator[] invalid index: %u, max entries %u, type size %u\n", (uint32_t)i, m_size, (uint32_t)sizeof(T));
+ abort();
+ }
+ return m_p[i];
+ }
+ BASISU_FORCE_INLINE T& operator[] (size_t i)
+ {
+ if (i >= m_size)
+ {
+ fprintf(stderr, "operator[] invalid index: %u, max entries %u, type size %u\n", (uint32_t)i, m_size, (uint32_t)sizeof(T));
+ abort();
+ }
+ return m_p[i];
+ }
+#endif
+
+ // at() always includes range checking, even in final builds, unlike operator [].
+ // The first element is returned if the index is out of range.
+ BASISU_FORCE_INLINE const T& at(size_t i) const { assert(i < m_size); return (i >= m_size) ? m_p[0] : m_p[i]; }
+ BASISU_FORCE_INLINE T& at(size_t i) { assert(i < m_size); return (i >= m_size) ? m_p[0] : m_p[i]; }
+
+#if !BASISU_VECTOR_FORCE_CHECKING
+ BASISU_FORCE_INLINE const T& front() const { assert(m_size); return m_p[0]; }
+ BASISU_FORCE_INLINE T& front() { assert(m_size); return m_p[0]; }
+
+ BASISU_FORCE_INLINE const T& back() const { assert(m_size); return m_p[m_size - 1]; }
+ BASISU_FORCE_INLINE T& back() { assert(m_size); return m_p[m_size - 1]; }
+#else
+ BASISU_FORCE_INLINE const T& front() const
+ {
+ if (!m_size)
+ {
+ fprintf(stderr, "front: vector is empty, type size %u\n", (uint32_t)sizeof(T));
+ abort();
+ }
+ return m_p[0];
+ }
+ BASISU_FORCE_INLINE T& front()
+ {
+ if (!m_size)
+ {
+ fprintf(stderr, "front: vector is empty, type size %u\n", (uint32_t)sizeof(T));
+ abort();
+ }
+ return m_p[0];
+ }
+
+ BASISU_FORCE_INLINE const T& back() const
+ {
+ if(!m_size)
+ {
+ fprintf(stderr, "back: vector is empty, type size %u\n", (uint32_t)sizeof(T));
+ abort();
+ }
+ return m_p[m_size - 1];
+ }
+ BASISU_FORCE_INLINE T& back()
+ {
+ if (!m_size)
+ {
+ fprintf(stderr, "back: vector is empty, type size %u\n", (uint32_t)sizeof(T));
+ abort();
+ }
+ return m_p[m_size - 1];
+ }
+#endif
+
+ BASISU_FORCE_INLINE const T* get_ptr() const { return m_p; }
+ BASISU_FORCE_INLINE T* get_ptr() { return m_p; }
+
+ BASISU_FORCE_INLINE const T* data() const { return m_p; }
+ BASISU_FORCE_INLINE T* data() { return m_p; }
+
+ // clear() sets the container to empty, then frees the allocated block.
+ inline void clear()
+ {
+ if (m_p)
+ {
+ scalar_type::destruct_array(m_p, m_size);
+ free(m_p);
+ m_p = NULL;
+ m_size = 0;
+ m_capacity = 0;
+ }
+ }
+
+ inline void clear_no_destruction()
+ {
+ if (m_p)
+ {
+ free(m_p);
+ m_p = NULL;
+ m_size = 0;
+ m_capacity = 0;
+ }
+ }
+
+ inline void reserve(size_t new_capacity_size_t)
+ {
+ if (new_capacity_size_t > UINT32_MAX)
+ {
+ assert(0);
+ return;
+ }
+
+ uint32_t new_capacity = (uint32_t)new_capacity_size_t;
+
+ if (new_capacity > m_capacity)
+ increase_capacity(new_capacity, false);
+ else if (new_capacity < m_capacity)
+ {
+ // Must work around the lack of a "decrease_capacity()" method.
+ // This case is rare enough in practice that it's probably not worth implementing an optimized in-place resize.
+ vector tmp;
+ tmp.increase_capacity(helpers::maximum(m_size, new_capacity), false);
+ tmp = *this;
+ swap(tmp);
+ }
+ }
+
+ inline bool try_reserve(size_t new_capacity_size_t)
+ {
+ if (new_capacity_size_t > UINT32_MAX)
+ {
+ assert(0);
+ return false;
+ }
+
+ uint32_t new_capacity = (uint32_t)new_capacity_size_t;
+
+ if (new_capacity > m_capacity)
+ {
+ if (!increase_capacity(new_capacity, false))
+ return false;
+ }
+ else if (new_capacity < m_capacity)
+ {
+ // Must work around the lack of a "decrease_capacity()" method.
+ // This case is rare enough in practice that it's probably not worth implementing an optimized in-place resize.
+ vector tmp;
+ tmp.increase_capacity(helpers::maximum(m_size, new_capacity), false);
+ tmp = *this;
+ swap(tmp);
+ }
+
+ return true;
+ }
+
+ // resize(0) sets the container to empty, but does not free the allocated block.
+ inline void resize(size_t new_size_size_t, bool grow_hint = false)
+ {
+ if (new_size_size_t > UINT32_MAX)
+ {
+ assert(0);
+ return;
+ }
+
+ uint32_t new_size = (uint32_t)new_size_size_t;
+
+ if (m_size != new_size)
+ {
+ if (new_size < m_size)
+ scalar_type::destruct_array(m_p + new_size, m_size - new_size);
+ else
+ {
+ if (new_size > m_capacity)
+ increase_capacity(new_size, (new_size == (m_size + 1)) || grow_hint);
+
+ scalar_type::construct_array(m_p + m_size, new_size - m_size);
+ }
+
+ m_size = new_size;
+ }
+ }
+
+ inline bool try_resize(size_t new_size_size_t, bool grow_hint = false)
+ {
+ if (new_size_size_t > UINT32_MAX)
+ {
+ assert(0);
+ return false;
+ }
+
+ uint32_t new_size = (uint32_t)new_size_size_t;
+
+ if (m_size != new_size)
+ {
+ if (new_size < m_size)
+ scalar_type::destruct_array(m_p + new_size, m_size - new_size);
+ else
+ {
+ if (new_size > m_capacity)
+ {
+ if (!increase_capacity(new_size, (new_size == (m_size + 1)) || grow_hint, true))
+ return false;
+ }
+
+ scalar_type::construct_array(m_p + m_size, new_size - m_size);
+ }
+
+ m_size = new_size;
+ }
+
+ return true;
+ }
+
+ // If size >= capacity/2, reset() sets the container's size to 0 but doesn't free the allocated block (because the container may be similarly loaded in the future).
+ // Otherwise it blows away the allocated block. See http://www.codercorner.com/blog/?p=494
+ inline void reset()
+ {
+ if (m_size >= (m_capacity >> 1))
+ resize(0);
+ else
+ clear();
+ }
+
+ inline T* enlarge(uint32_t i)
+ {
+ uint32_t cur_size = m_size;
+ resize(cur_size + i, true);
+ return get_ptr() + cur_size;
+ }
+
+ inline T* try_enlarge(uint32_t i)
+ {
+ uint32_t cur_size = m_size;
+ if (!try_resize(cur_size + i, true))
+ return NULL;
+ return get_ptr() + cur_size;
+ }
+
+ BASISU_FORCE_INLINE void push_back(const T& obj)
+ {
+ assert(!m_p || (&obj < m_p) || (&obj >= (m_p + m_size)));
+
+ if (m_size >= m_capacity)
+ increase_capacity(m_size + 1, true);
+
+ scalar_type::construct(m_p + m_size, obj);
+ m_size++;
+ }
+
+ inline bool try_push_back(const T& obj)
+ {
+ assert(!m_p || (&obj < m_p) || (&obj >= (m_p + m_size)));
+
+ if (m_size >= m_capacity)
+ {
+ if (!increase_capacity(m_size + 1, true, true))
+ return false;
+ }
+
+ scalar_type::construct(m_p + m_size, obj);
+ m_size++;
+
+ return true;
+ }
+
+ inline void push_back_value(T obj)
+ {
+ if (m_size >= m_capacity)
+ increase_capacity(m_size + 1, true);
+
+ scalar_type::construct(m_p + m_size, obj);
+ m_size++;
+ }
+
+ inline void pop_back()
+ {
+ assert(m_size);
+
+ if (m_size)
+ {
+ m_size--;
+ scalar_type::destruct(&m_p[m_size]);
+ }
+ }
+
+ inline void insert(uint32_t index, const T* p, uint32_t n)
+ {
+ assert(index <= m_size);
+ if (!n)
+ return;
+
+ const uint32_t orig_size = m_size;
+ resize(m_size + n, true);
+
+ const uint32_t num_to_move = orig_size - index;
+
+ if (BASISU_IS_BITWISE_COPYABLE(T))
+ {
+ // This overwrites the destination object bits, but bitwise copyable means we don't need to worry about destruction.
+ memmove(m_p + index + n, m_p + index, sizeof(T) * num_to_move);
+ }
+ else
+ {
+ const T* pSrc = m_p + orig_size - 1;
+ T* pDst = const_cast(pSrc) + n;
+
+ for (uint32_t i = 0; i < num_to_move; i++)
+ {
+ assert((pDst - m_p) < (int)m_size);
+ *pDst-- = *pSrc--;
+ }
+ }
+
+ T* pDst = m_p + index;
+
+ if (BASISU_IS_BITWISE_COPYABLE(T))
+ {
+ // This copies in the new bits, overwriting the existing objects, which is OK for copyable types that don't need destruction.
+ memcpy(pDst, p, sizeof(T) * n);
+ }
+ else
+ {
+ for (uint32_t i = 0; i < n; i++)
+ {
+ assert((pDst - m_p) < (int)m_size);
+ *pDst++ = *p++;
+ }
+ }
+ }
+
+ inline void insert(T* p, const T& obj)
+ {
+ int64_t ofs = p - begin();
+ if ((ofs < 0) || (ofs > UINT32_MAX))
+ {
+ assert(0);
+ return;
+ }
+
+ insert((uint32_t)ofs, &obj, 1);
+ }
+
+ // push_front() isn't going to be very fast - it's only here for usability.
+ inline void push_front(const T& obj)
+ {
+ insert(0, &obj, 1);
+ }
+
+ vector& append(const vector& other)
+ {
+ if (other.m_size)
+ insert(m_size, &other[0], other.m_size);
+ return *this;
+ }
+
+ vector& append(const T* p, uint32_t n)
+ {
+ if (n)
+ insert(m_size, p, n);
+ return *this;
+ }
+
+ inline void erase(uint32_t start, uint32_t n)
+ {
+ assert((start + n) <= m_size);
+ if ((start + n) > m_size)
+ return;
+
+ if (!n)
+ return;
+
+ const uint32_t num_to_move = m_size - (start + n);
+
+ T* pDst = m_p + start;
+
+ const T* pSrc = m_p + start + n;
+
+ if (BASISU_IS_BITWISE_COPYABLE_OR_MOVABLE(T))
+ {
+ // This test is overly cautious.
+ if ((!BASISU_IS_BITWISE_COPYABLE(T)) || (BASISU_HAS_DESTRUCTOR(T)))
+ {
+ // Type has been marked explictly as bitwise movable, which means we can move them around but they may need to be destructed.
+ // First destroy the erased objects.
+ scalar_type::destruct_array(pDst, n);
+ }
+
+ // Copy "down" the objects to preserve, filling in the empty slots.
+ memmove(pDst, pSrc, num_to_move * sizeof(T));
+ }
+ else
+ {
+ // Type is not bitwise copyable or movable.
+ // Move them down one at a time by using the equals operator, and destroying anything that's left over at the end.
+ T* pDst_end = pDst + num_to_move;
+ while (pDst != pDst_end)
+ *pDst++ = *pSrc++;
+
+ scalar_type::destruct_array(pDst_end, n);
+ }
+
+ m_size -= n;
+ }
+
+ inline void erase(uint32_t index)
+ {
+ erase(index, 1);
+ }
+
+ inline void erase(T* p)
+ {
+ assert((p >= m_p) && (p < (m_p + m_size)));
+ erase(static_cast(p - m_p));
+ }
+
+ inline void erase(T *pFirst, T *pEnd)
+ {
+ assert(pFirst <= pEnd);
+ assert(pFirst >= begin() && pFirst <= end());
+ assert(pEnd >= begin() && pEnd <= end());
+
+ int64_t ofs = pFirst - begin();
+ if ((ofs < 0) || (ofs > UINT32_MAX))
+ {
+ assert(0);
+ return;
+ }
+
+ int64_t n = pEnd - pFirst;
+ if ((n < 0) || (n > UINT32_MAX))
+ {
+ assert(0);
+ return;
+ }
+
+ erase((uint32_t)ofs, (uint32_t)n);
+ }
+
+ void erase_unordered(uint32_t index)
+ {
+ assert(index < m_size);
+
+ if ((index + 1) < m_size)
+ (*this)[index] = back();
+
+ pop_back();
+ }
+
+ inline bool operator== (const vector& rhs) const
+ {
+ if (m_size != rhs.m_size)
+ return false;
+ else if (m_size)
+ {
+ if (scalar_type::cFlag)
+ return memcmp(m_p, rhs.m_p, sizeof(T) * m_size) == 0;
+ else
+ {
+ const T* pSrc = m_p;
+ const T* pDst = rhs.m_p;
+ for (uint32_t i = m_size; i; i--)
+ if (!(*pSrc++ == *pDst++))
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ inline bool operator< (const vector& rhs) const
+ {
+ const uint32_t min_size = helpers::minimum(m_size, rhs.m_size);
+
+ const T* pSrc = m_p;
+ const T* pSrc_end = m_p + min_size;
+ const T* pDst = rhs.m_p;
+
+ while ((pSrc < pSrc_end) && (*pSrc == *pDst))
+ {
+ pSrc++;
+ pDst++;
+ }
+
+ if (pSrc < pSrc_end)
+ return *pSrc < *pDst;
+
+ return m_size < rhs.m_size;
+ }
+
+ inline void swap(vector& other)
+ {
+ std::swap(m_p, other.m_p);
+ std::swap(m_size, other.m_size);
+ std::swap(m_capacity, other.m_capacity);
+ }
+
+ inline void sort()
+ {
+ std::sort(begin(), end());
+ }
+
+ inline void unique()
+ {
+ if (!empty())
+ {
+ sort();
+
+ resize(std::unique(begin(), end()) - begin());
+ }
+ }
+
+ inline void reverse()
+ {
+ uint32_t j = m_size >> 1;
+ for (uint32_t i = 0; i < j; i++)
+ std::swap(m_p[i], m_p[m_size - 1 - i]);
+ }
+
+ inline int find(const T& key) const
+ {
+ const T* p = m_p;
+ const T* p_end = m_p + m_size;
+
+ uint32_t index = 0;
+
+ while (p != p_end)
+ {
+ if (key == *p)
+ return index;
+
+ p++;
+ index++;
+ }
+
+ return cInvalidIndex;
+ }
+
+ inline int find_sorted(const T& key) const
+ {
+ if (m_size)
+ {
+ // Uniform binary search - Knuth Algorithm 6.2.1 U, unrolled twice.
+ int i = ((m_size + 1) >> 1) - 1;
+ int m = m_size;
+
+ for (; ; )
+ {
+ assert(i >= 0 && i < (int)m_size);
+ const T* pKey_i = m_p + i;
+ int cmp = key < *pKey_i;
+#if defined(_DEBUG) || defined(DEBUG)
+ int cmp2 = *pKey_i < key;
+ assert((cmp != cmp2) || (key == *pKey_i));
+#endif
+ if ((!cmp) && (key == *pKey_i)) return i;
+ m >>= 1;
+ if (!m) break;
+ cmp = -cmp;
+ i += (((m + 1) >> 1) ^ cmp) - cmp;
+ if (i < 0)
+ break;
+
+ assert(i >= 0 && i < (int)m_size);
+ pKey_i = m_p + i;
+ cmp = key < *pKey_i;
+#if defined(_DEBUG) || defined(DEBUG)
+ cmp2 = *pKey_i < key;
+ assert((cmp != cmp2) || (key == *pKey_i));
+#endif
+ if ((!cmp) && (key == *pKey_i)) return i;
+ m >>= 1;
+ if (!m) break;
+ cmp = -cmp;
+ i += (((m + 1) >> 1) ^ cmp) - cmp;
+ if (i < 0)
+ break;
+ }
+ }
+
+ return cInvalidIndex;
+ }
+
+ template
+ inline int find_sorted(const T& key, Q less_than) const
+ {
+ if (m_size)
+ {
+ // Uniform binary search - Knuth Algorithm 6.2.1 U, unrolled twice.
+ int i = ((m_size + 1) >> 1) - 1;
+ int m = m_size;
+
+ for (; ; )
+ {
+ assert(i >= 0 && i < (int)m_size);
+ const T* pKey_i = m_p + i;
+ int cmp = less_than(key, *pKey_i);
+ if ((!cmp) && (!less_than(*pKey_i, key))) return i;
+ m >>= 1;
+ if (!m) break;
+ cmp = -cmp;
+ i += (((m + 1) >> 1) ^ cmp) - cmp;
+ if (i < 0)
+ break;
+
+ assert(i >= 0 && i < (int)m_size);
+ pKey_i = m_p + i;
+ cmp = less_than(key, *pKey_i);
+ if ((!cmp) && (!less_than(*pKey_i, key))) return i;
+ m >>= 1;
+ if (!m) break;
+ cmp = -cmp;
+ i += (((m + 1) >> 1) ^ cmp) - cmp;
+ if (i < 0)
+ break;
+ }
+ }
+
+ return cInvalidIndex;
+ }
+
+ inline uint32_t count_occurences(const T& key) const
+ {
+ uint32_t c = 0;
+
+ const T* p = m_p;
+ const T* p_end = m_p + m_size;
+
+ while (p != p_end)
+ {
+ if (key == *p)
+ c++;
+
+ p++;
+ }
+
+ return c;
+ }
+
+ inline void set_all(const T& o)
+ {
+ if ((sizeof(T) == 1) && (scalar_type::cFlag))
+ memset(m_p, *reinterpret_cast(&o), m_size);
+ else
+ {
+ T* pDst = m_p;
+ T* pDst_end = pDst + m_size;
+ while (pDst != pDst_end)
+ *pDst++ = o;
+ }
+ }
+
+ // Caller assumes ownership of the heap block associated with the container. Container is cleared.
+ inline void* assume_ownership()
+ {
+ T* p = m_p;
+ m_p = NULL;
+ m_size = 0;
+ m_capacity = 0;
+ return p;
+ }
+
+ // Caller is granting ownership of the indicated heap block.
+ // Block must have size constructed elements, and have enough room for capacity elements.
+ // The block must have been allocated using malloc().
+ // Important: This method is used in Basis Universal. If you change how this container allocates memory, you'll need to change any users of this method.
+ inline bool grant_ownership(T* p, uint32_t size, uint32_t capacity)
+ {
+ // To to prevent the caller from obviously shooting themselves in the foot.
+ if (((p + capacity) > m_p) && (p < (m_p + m_capacity)))
+ {
+ // Can grant ownership of a block inside the container itself!
+ assert(0);
+ return false;
+ }
+
+ if (size > capacity)
+ {
+ assert(0);
+ return false;
+ }
+
+ if (!p)
+ {
+ if (capacity)
+ {
+ assert(0);
+ return false;
+ }
+ }
+ else if (!capacity)
+ {
+ assert(0);
+ return false;
+ }
+
+ clear();
+ m_p = p;
+ m_size = size;
+ m_capacity = capacity;
+ return true;
+ }
+
+ private:
+ T* m_p;
+ uint32_t m_size;
+ uint32_t m_capacity;
+
+ template struct is_vector { enum { cFlag = false }; };
+ template struct is_vector< vector > { enum { cFlag = true }; };
+
+ static void object_mover(void* pDst_void, void* pSrc_void, uint32_t num)
+ {
+ T* pSrc = static_cast(pSrc_void);
+ T* const pSrc_end = pSrc + num;
+ T* pDst = static_cast(pDst_void);
+
+ while (pSrc != pSrc_end)
+ {
+ // placement new
+ new (static_cast(pDst)) T(*pSrc);
+ pSrc->~T();
+ ++pSrc;
+ ++pDst;
+ }
+ }
+
+ inline bool increase_capacity(uint32_t min_new_capacity, bool grow_hint, bool nofail = false)
+ {
+ return reinterpret_cast(this)->increase_capacity(
+ min_new_capacity, grow_hint, sizeof(T),
+ (BASISU_IS_BITWISE_COPYABLE_OR_MOVABLE(T) || (is_vector::cFlag)) ? NULL : object_mover, nofail);
+ }
+ };
+
+ template struct bitwise_movable< vector > { enum { cFlag = true }; };
+
+ // Hash map
+
+ template
+ struct hasher
+ {
+ inline size_t operator() (const T& key) const { return static_cast(key); }
+ };
+
+ template
+ struct equal_to
+ {
+ inline bool operator()(const T& a, const T& b) const { return a == b; }
+ };
+
+ // Important: The Hasher and Equals objects must be bitwise movable!
+ template, typename Equals = equal_to >
+ class hash_map
+ {
+ public:
+ class iterator;
+ class const_iterator;
+
+ private:
+ friend class iterator;
+ friend class const_iterator;
+
+ enum state
+ {
+ cStateInvalid = 0,
+ cStateValid = 1
+ };
+
+ enum
+ {
+ cMinHashSize = 4U
+ };
+
+ public:
+ typedef hash_map hash_map_type;
+ typedef std::pair value_type;
+ typedef Key key_type;
+ typedef Value referent_type;
+ typedef Hasher hasher_type;
+ typedef Equals equals_type;
+
+ hash_map() :
+ m_hash_shift(32), m_num_valid(0), m_grow_threshold(0)
+ {
+ }
+
+ hash_map(const hash_map& other) :
+ m_values(other.m_values),
+ m_hash_shift(other.m_hash_shift),
+ m_hasher(other.m_hasher),
+ m_equals(other.m_equals),
+ m_num_valid(other.m_num_valid),
+ m_grow_threshold(other.m_grow_threshold)
+ {
+ }
+
+ hash_map& operator= (const hash_map& other)
+ {
+ if (this == &other)
+ return *this;
+
+ clear();
+
+ m_values = other.m_values;
+ m_hash_shift = other.m_hash_shift;
+ m_num_valid = other.m_num_valid;
+ m_grow_threshold = other.m_grow_threshold;
+ m_hasher = other.m_hasher;
+ m_equals = other.m_equals;
+
+ return *this;
+ }
+
+ inline ~hash_map()
+ {
+ clear();
+ }
+
+ const Equals& get_equals() const { return m_equals; }
+ Equals& get_equals() { return m_equals; }
+
+ void set_equals(const Equals& equals) { m_equals = equals; }
+
+ const Hasher& get_hasher() const { return m_hasher; }
+ Hasher& get_hasher() { return m_hasher; }
+
+ void set_hasher(const Hasher& hasher) { m_hasher = hasher; }
+
+ inline void clear()
+ {
+ if (!m_values.empty())
+ {
+ if (BASISU_HAS_DESTRUCTOR(Key) || BASISU_HAS_DESTRUCTOR(Value))
+ {
+ node* p = &get_node(0);
+ node* p_end = p + m_values.size();
+
+ uint32_t num_remaining = m_num_valid;
+ while (p != p_end)
+ {
+ if (p->state)
+ {
+ destruct_value_type(p);
+ num_remaining--;
+ if (!num_remaining)
+ break;
+ }
+
+ p++;
+ }
+ }
+
+ m_values.clear_no_destruction();
+
+ m_hash_shift = 32;
+ m_num_valid = 0;
+ m_grow_threshold = 0;
+ }
+ }
+
+ inline void reset()
+ {
+ if (!m_num_valid)
+ return;
+
+ if (BASISU_HAS_DESTRUCTOR(Key) || BASISU_HAS_DESTRUCTOR(Value))
+ {
+ node* p = &get_node(0);
+ node* p_end = p + m_values.size();
+
+ uint32_t num_remaining = m_num_valid;
+ while (p != p_end)
+ {
+ if (p->state)
+ {
+ destruct_value_type(p);
+ p->state = cStateInvalid;
+
+ num_remaining--;
+ if (!num_remaining)
+ break;
+ }
+
+ p++;
+ }
+ }
+ else if (sizeof(node) <= 32)
+ {
+ memset(&m_values[0], 0, m_values.size_in_bytes());
+ }
+ else
+ {
+ node* p = &get_node(0);
+ node* p_end = p + m_values.size();
+
+ uint32_t num_remaining = m_num_valid;
+ while (p != p_end)
+ {
+ if (p->state)
+ {
+ p->state = cStateInvalid;
+
+ num_remaining--;
+ if (!num_remaining)
+ break;
+ }
+
+ p++;
+ }
+ }
+
+ m_num_valid = 0;
+ }
+
+ inline uint32_t size()
+ {
+ return m_num_valid;
+ }
+
+ inline uint32_t get_table_size()
+ {
+ return m_values.size();
+ }
+
+ inline bool empty()
+ {
+ return !m_num_valid;
+ }
+
+ inline void reserve(uint32_t new_capacity)
+ {
+ uint64_t new_hash_size = helpers::maximum(1U, new_capacity);
+
+ new_hash_size = new_hash_size * 2ULL;
+
+ if (!helpers::is_power_of_2(new_hash_size))
+ new_hash_size = helpers::next_pow2(new_hash_size);
+
+ new_hash_size = helpers::maximum(cMinHashSize, new_hash_size);
+
+ new_hash_size = helpers::minimum(0x80000000UL, new_hash_size);
+
+ if (new_hash_size > m_values.size())
+ rehash((uint32_t)new_hash_size);
+ }
+
+ class iterator
+ {
+ friend class hash_map;
+ friend class hash_map::const_iterator;
+
+ public:
+ inline iterator() : m_pTable(NULL), m_index(0) { }
+ inline iterator(hash_map_type& table, uint32_t index) : m_pTable(&table), m_index(index) { }
+ inline iterator(const iterator& other) : m_pTable(other.m_pTable), m_index(other.m_index) { }
+
+ inline iterator& operator= (const iterator& other)
+ {
+ m_pTable = other.m_pTable;
+ m_index = other.m_index;
+ return *this;
+ }
+
+ // post-increment
+ inline iterator operator++(int)
+ {
+ iterator result(*this);
+ ++*this;
+ return result;
+ }
+
+ // pre-increment
+ inline iterator& operator++()
+ {
+ probe();
+ return *this;
+ }
+
+ inline value_type& operator*() const { return *get_cur(); }
+ inline value_type* operator->() const { return get_cur(); }
+
+ inline bool operator == (const iterator& b) const { return (m_pTable == b.m_pTable) && (m_index == b.m_index); }
+ inline bool operator != (const iterator& b) const { return !(*this == b); }
+ inline bool operator == (const const_iterator& b) const { return (m_pTable == b.m_pTable) && (m_index == b.m_index); }
+ inline bool operator != (const const_iterator& b) const { return !(*this == b); }
+
+ private:
+ hash_map_type* m_pTable;
+ uint32_t m_index;
+
+ inline value_type* get_cur() const
+ {
+ assert(m_pTable && (m_index < m_pTable->m_values.size()));
+ assert(m_pTable->get_node_state(m_index) == cStateValid);
+
+ return &m_pTable->get_node(m_index);
+ }
+
+ inline void probe()
+ {
+ assert(m_pTable);
+ m_index = m_pTable->find_next(m_index);
+ }
+ };
+
+ class const_iterator
+ {
+ friend class hash_map;
+ friend class hash_map::iterator;
+
+ public:
+ inline const_iterator() : m_pTable(NULL), m_index(0) { }
+ inline const_iterator(const hash_map_type& table, uint32_t index) : m_pTable(&table), m_index(index) { }
+ inline const_iterator(const iterator& other) : m_pTable(other.m_pTable), m_index(other.m_index) { }
+ inline const_iterator(const const_iterator& other) : m_pTable(other.m_pTable), m_index(other.m_index) { }
+
+ inline const_iterator& operator= (const const_iterator& other)
+ {
+ m_pTable = other.m_pTable;
+ m_index = other.m_index;
+ return *this;
+ }
+
+ inline const_iterator& operator= (const iterator& other)
+ {
+ m_pTable = other.m_pTable;
+ m_index = other.m_index;
+ return *this;
+ }
+
+ // post-increment
+ inline const_iterator operator++(int)
+ {
+ const_iterator result(*this);
+ ++*this;
+ return result;
+ }
+
+ // pre-increment
+ inline const_iterator& operator++()
+ {
+ probe();
+ return *this;
+ }
+
+ inline const value_type& operator*() const { return *get_cur(); }
+ inline const value_type* operator->() const { return get_cur(); }
+
+ inline bool operator == (const const_iterator& b) const { return (m_pTable == b.m_pTable) && (m_index == b.m_index); }
+ inline bool operator != (const const_iterator& b) const { return !(*this == b); }
+ inline bool operator == (const iterator& b) const { return (m_pTable == b.m_pTable) && (m_index == b.m_index); }
+ inline bool operator != (const iterator& b) const { return !(*this == b); }
+
+ private:
+ const hash_map_type* m_pTable;
+ uint32_t m_index;
+
+ inline const value_type* get_cur() const
+ {
+ assert(m_pTable && (m_index < m_pTable->m_values.size()));
+ assert(m_pTable->get_node_state(m_index) == cStateValid);
+
+ return &m_pTable->get_node(m_index);
+ }
+
+ inline void probe()
+ {
+ assert(m_pTable);
+ m_index = m_pTable->find_next(m_index);
+ }
+ };
+
+ inline const_iterator begin() const
+ {
+ if (!m_num_valid)
+ return end();
+
+ return const_iterator(*this, find_next(UINT32_MAX));
+ }
+
+ inline const_iterator end() const
+ {
+ return const_iterator(*this, m_values.size());
+ }
+
+ inline iterator begin()
+ {
+ if (!m_num_valid)
+ return end();
+
+ return iterator(*this, find_next(UINT32_MAX));
+ }
+
+ inline iterator end()
+ {
+ return iterator(*this, m_values.size());
+ }
+
+ // insert_result.first will always point to inserted key/value (or the already existing key/value).
+ // insert_resutt.second will be true if a new key/value was inserted, or false if the key already existed (in which case first will point to the already existing value).
+ typedef std::pair insert_result;
+
+ inline insert_result insert(const Key& k, const Value& v = Value())
+ {
+ insert_result result;
+ if (!insert_no_grow(result, k, v))
+ {
+ grow();
+
+ // This must succeed.
+ if (!insert_no_grow(result, k, v))
+ {
+ fprintf(stderr, "insert() failed");
+ abort();
+ }
+ }
+
+ return result;
+ }
+
+ inline insert_result insert(const value_type& v)
+ {
+ return insert(v.first, v.second);
+ }
+
+ inline const_iterator find(const Key& k) const
+ {
+ return const_iterator(*this, find_index(k));
+ }
+
+ inline iterator find(const Key& k)
+ {
+ return iterator(*this, find_index(k));
+ }
+
+ inline bool erase(const Key& k)
+ {
+ uint32_t i = find_index(k);
+
+ if (i >= m_values.size())
+ return false;
+
+ node* pDst = &get_node(i);
+ destruct_value_type(pDst);
+ pDst->state = cStateInvalid;
+
+ m_num_valid--;
+
+ for (; ; )
+ {
+ uint32_t r, j = i;
+
+ node* pSrc = pDst;
+
+ do
+ {
+ if (!i)
+ {
+ i = m_values.size() - 1;
+ pSrc = &get_node(i);
+ }
+ else
+ {
+ i--;
+ pSrc--;
+ }
+
+ if (!pSrc->state)
+ return true;
+
+ r = hash_key(pSrc->first);
+
+ } while ((i <= r && r < j) || (r < j && j < i) || (j < i && i <= r));
+
+ move_node(pDst, pSrc);
+
+ pDst = pSrc;
+ }
+ }
+
+ inline void swap(hash_map_type& other)
+ {
+ m_values.swap(other.m_values);
+ std::swap(m_hash_shift, other.m_hash_shift);
+ std::swap(m_num_valid, other.m_num_valid);
+ std::swap(m_grow_threshold, other.m_grow_threshold);
+ std::swap(m_hasher, other.m_hasher);
+ std::swap(m_equals, other.m_equals);
+ }
+
+ private:
+ struct node : public value_type
+ {
+ uint8_t state;
+ };
+
+ static inline void construct_value_type(value_type* pDst, const Key& k, const Value& v)
+ {
+ if (BASISU_IS_BITWISE_COPYABLE(Key))
+ memcpy(&pDst->first, &k, sizeof(Key));
+ else
+ scalar_type::construct(&pDst->first, k);
+
+ if (BASISU_IS_BITWISE_COPYABLE(Value))
+ memcpy(&pDst->second, &v, sizeof(Value));
+ else
+ scalar_type::construct(&pDst->second, v);
+ }
+
+ static inline void construct_value_type(value_type* pDst, const value_type* pSrc)
+ {
+ if ((BASISU_IS_BITWISE_COPYABLE(Key)) && (BASISU_IS_BITWISE_COPYABLE(Value)))
+ {
+ memcpy(pDst, pSrc, sizeof(value_type));
+ }
+ else
+ {
+ if (BASISU_IS_BITWISE_COPYABLE(Key))
+ memcpy(&pDst->first, &pSrc->first, sizeof(Key));
+ else
+ scalar_type::construct(&pDst->first, pSrc->first);
+
+ if (BASISU_IS_BITWISE_COPYABLE(Value))
+ memcpy(&pDst->second, &pSrc->second, sizeof(Value));
+ else
+ scalar_type::construct(&pDst->second, pSrc->second);
+ }
+ }
+
+ static inline void destruct_value_type(value_type* p)
+ {
+ scalar_type::destruct(&p->first);
+ scalar_type::destruct(&p->second);
+ }
+
+ // Moves *pSrc to *pDst efficiently.
+ // pDst should NOT be constructed on entry.
+ static inline void move_node(node* pDst, node* pSrc, bool update_src_state = true)
+ {
+ assert(!pDst->state);
+
+ if (BASISU_IS_BITWISE_COPYABLE_OR_MOVABLE(Key) && BASISU_IS_BITWISE_COPYABLE_OR_MOVABLE(Value))
+ {
+ memcpy(pDst, pSrc, sizeof(node));
+ }
+ else
+ {
+ if (BASISU_IS_BITWISE_COPYABLE_OR_MOVABLE(Key))
+ memcpy(&pDst->first, &pSrc->first, sizeof(Key));
+ else
+ {
+ scalar_type::construct(&pDst->first, pSrc->first);
+ scalar_type::destruct(&pSrc->first);
+ }
+
+ if (BASISU_IS_BITWISE_COPYABLE_OR_MOVABLE(Value))
+ memcpy(&pDst->second, &pSrc->second, sizeof(Value));
+ else
+ {
+ scalar_type::construct(&pDst->second, pSrc->second);
+ scalar_type::destruct(&pSrc->second);
+ }
+
+ pDst->state = cStateValid;
+ }
+
+ if (update_src_state)
+ pSrc->state = cStateInvalid;
+ }
+
+ struct raw_node
+ {
+ inline raw_node()
+ {
+ node* p = reinterpret_cast(this);
+ p->state = cStateInvalid;
+ }
+
+ inline ~raw_node()
+ {
+ node* p = reinterpret_cast(this);
+ if (p->state)
+ hash_map_type::destruct_value_type(p);
+ }
+
+ inline raw_node(const raw_node& other)
+ {
+ node* pDst = reinterpret_cast(this);
+ const node* pSrc = reinterpret_cast(&other);
+
+ if (pSrc->state)
+ {
+ hash_map_type::construct_value_type(pDst, pSrc);
+ pDst->state = cStateValid;
+ }
+ else
+ pDst->state = cStateInvalid;
+ }
+
+ inline raw_node& operator= (const raw_node& rhs)
+ {
+ if (this == &rhs)
+ return *this;
+
+ node* pDst = reinterpret_cast(this);
+ const node* pSrc = reinterpret_cast(&rhs);
+
+ if (pSrc->state)
+ {
+ if (pDst->state)
+ {
+ pDst->first = pSrc->first;
+ pDst->second = pSrc->second;
+ }
+ else
+ {
+ hash_map_type::construct_value_type(pDst, pSrc);
+ pDst->state = cStateValid;
+ }
+ }
+ else if (pDst->state)
+ {
+ hash_map_type::destruct_value_type(pDst);
+ pDst->state = cStateInvalid;
+ }
+
+ return *this;
+ }
+
+ uint8_t m_bits[sizeof(node)];
+ };
+
+ typedef basisu::vector node_vector;
+
+ node_vector m_values;
+ uint32_t m_hash_shift;
+
+ Hasher m_hasher;
+ Equals m_equals;
+
+ uint32_t m_num_valid;
+
+ uint32_t m_grow_threshold;
+
+ inline uint32_t hash_key(const Key& k) const
+ {
+ assert((1U << (32U - m_hash_shift)) == m_values.size());
+
+ uint32_t hash = static_cast(m_hasher(k));
+
+ // Fibonacci hashing
+ hash = (2654435769U * hash) >> m_hash_shift;
+
+ assert(hash < m_values.size());
+ return hash;
+ }
+
+ inline const node& get_node(uint32_t index) const
+ {
+ return *reinterpret_cast(&m_values[index]);
+ }
+
+ inline node& get_node(uint32_t index)
+ {
+ return *reinterpret_cast(&m_values[index]);
+ }
+
+ inline state get_node_state(uint32_t index) const
+ {
+ return static_cast(get_node(index).state);
+ }
+
+ inline void set_node_state(uint32_t index, bool valid)
+ {
+ get_node(index).state = valid;
+ }
+
+ inline void grow()
+ {
+ uint64_t n = m_values.size() * 3ULL; // was * 2
+
+ if (!helpers::is_power_of_2(n))
+ n = helpers::next_pow2(n);
+
+ if (n > 0x80000000UL)
+ n = 0x80000000UL;
+
+ rehash(helpers::maximum(cMinHashSize, (uint32_t)n));
+ }
+
+ inline void rehash(uint32_t new_hash_size)
+ {
+ assert(new_hash_size >= m_num_valid);
+ assert(helpers::is_power_of_2(new_hash_size));
+
+ if ((new_hash_size < m_num_valid) || (new_hash_size == m_values.size()))
+ return;
+
+ hash_map new_map;
+ new_map.m_values.resize(new_hash_size);
+ new_map.m_hash_shift = 32U - helpers::floor_log2i(new_hash_size);
+ assert(new_hash_size == (1U << (32U - new_map.m_hash_shift)));
+ new_map.m_grow_threshold = UINT_MAX;
+
+ node* pNode = reinterpret_cast(m_values.begin());
+ node* pNode_end = pNode + m_values.size();
+
+ while (pNode != pNode_end)
+ {
+ if (pNode->state)
+ {
+ new_map.move_into(pNode);
+
+ if (new_map.m_num_valid == m_num_valid)
+ break;
+ }
+
+ pNode++;
+ }
+
+ new_map.m_grow_threshold = (new_hash_size + 1U) >> 1U;
+
+ m_values.clear_no_destruction();
+ m_hash_shift = 32;
+
+ swap(new_map);
+ }
+
+ inline uint32_t find_next(uint32_t index) const
+ {
+ index++;
+
+ if (index >= m_values.size())
+ return index;
+
+ const node* pNode = &get_node(index);
+
+ for (; ; )
+ {
+ if (pNode->state)
+ break;
+
+ if (++index >= m_values.size())
+ break;
+
+ pNode++;
+ }
+
+ return index;
+ }
+
+ inline uint32_t find_index(const Key& k) const
+ {
+ if (m_num_valid)
+ {
+ uint32_t index = hash_key(k);
+ const node* pNode = &get_node(index);
+
+ if (pNode->state)
+ {
+ if (m_equals(pNode->first, k))
+ return index;
+
+ const uint32_t orig_index = index;
+
+ for (; ; )
+ {
+ if (!index)
+ {
+ index = m_values.size() - 1;
+ pNode = &get_node(index);
+ }
+ else
+ {
+ index--;
+ pNode--;
+ }
+
+ if (index == orig_index)
+ break;
+
+ if (!pNode->state)
+ break;
+
+ if (m_equals(pNode->first, k))
+ return index;
+ }
+ }
+ }
+
+ return m_values.size();
+ }
+
+ inline bool insert_no_grow(insert_result& result, const Key& k, const Value& v = Value())
+ {
+ if (!m_values.size())
+ return false;
+
+ uint32_t index = hash_key(k);
+ node* pNode = &get_node(index);
+
+ if (pNode->state)
+ {
+ if (m_equals(pNode->first, k))
+ {
+ result.first = iterator(*this, index);
+ result.second = false;
+ return true;
+ }
+
+ const uint32_t orig_index = index;
+
+ for (; ; )
+ {
+ if (!index)
+ {
+ index = m_values.size() - 1;
+ pNode = &get_node(index);
+ }
+ else
+ {
+ index--;
+ pNode--;
+ }
+
+ if (orig_index == index)
+ return false;
+
+ if (!pNode->state)
+ break;
+
+ if (m_equals(pNode->first, k))
+ {
+ result.first = iterator(*this, index);
+ result.second = false;
+ return true;
+ }
+ }
+ }
+
+ if (m_num_valid >= m_grow_threshold)
+ return false;
+
+ construct_value_type(pNode, k, v);
+
+ pNode->state = cStateValid;
+
+ m_num_valid++;
+ assert(m_num_valid <= m_values.size());
+
+ result.first = iterator(*this, index);
+ result.second = true;
+
+ return true;
+ }
+
+ inline void move_into(node* pNode)
+ {
+ uint32_t index = hash_key(pNode->first);
+ node* pDst_node = &get_node(index);
+
+ if (pDst_node->state)
+ {
+ const uint32_t orig_index = index;
+
+ for (; ; )
+ {
+ if (!index)
+ {
+ index = m_values.size() - 1;
+ pDst_node = &get_node(index);
+ }
+ else
+ {
+ index--;
+ pDst_node--;
+ }
+
+ if (index == orig_index)
+ {
+ assert(false);
+ return;
+ }
+
+ if (!pDst_node->state)
+ break;
+ }
+ }
+
+ move_node(pDst_node, pNode, false);
+
+ m_num_valid++;
+ }
+ };
+
+ template
+ struct bitwise_movable< hash_map > { enum { cFlag = true }; };
+
+#if BASISU_HASHMAP_TEST
+ extern void hash_map_test();
+#endif
+
+} // namespace basisu
+
+namespace std
+{
+ template
+ inline void swap(basisu::vector& a, basisu::vector& b)
+ {
+ a.swap(b);
+ }
+
+ template
+ inline void swap(basisu::hash_map& a, basisu::hash_map& b)
+ {
+ a.swap(b);
+ }
+
+} // namespace std
diff --git a/ext/basis_universal/basisu_containers_impl.h b/ext/basis_universal/basisu_containers_impl.h
new file mode 100644
index 0000000000..d5cb61569b
--- /dev/null
+++ b/ext/basis_universal/basisu_containers_impl.h
@@ -0,0 +1,315 @@
+// basisu_containers_impl.h
+// Do not include directly
+
+#ifdef _MSC_VER
+#pragma warning (disable:4127) // warning C4127: conditional expression is constant
+#endif
+
+namespace basisu
+{
+ bool elemental_vector::increase_capacity(uint32_t min_new_capacity, bool grow_hint, uint32_t element_size, object_mover pMover, bool nofail)
+ {
+ assert(m_size <= m_capacity);
+
+ if (sizeof(void *) == sizeof(uint64_t))
+ assert(min_new_capacity < (0x400000000ULL / element_size));
+ else
+ assert(min_new_capacity < (0x7FFF0000U / element_size));
+
+ if (m_capacity >= min_new_capacity)
+ return true;
+
+ size_t new_capacity = min_new_capacity;
+ if ((grow_hint) && (!helpers::is_power_of_2((uint64_t)new_capacity)))
+ {
+ new_capacity = (size_t)helpers::next_pow2((uint64_t)new_capacity);
+
+ assert(new_capacity && (new_capacity > m_capacity));
+
+ if (new_capacity < min_new_capacity)
+ {
+ if (nofail)
+ return false;
+ fprintf(stderr, "vector too large\n");
+ abort();
+ }
+ }
+
+ const size_t desired_size = element_size * new_capacity;
+ size_t actual_size = 0;
+ if (!pMover)
+ {
+ void* new_p = realloc(m_p, desired_size);
+ if (!new_p)
+ {
+ if (nofail)
+ return false;
+
+ char buf[256];
+#ifdef _MSC_VER
+ sprintf_s(buf, sizeof(buf), "vector: realloc() failed allocating %u bytes", (uint32_t)desired_size);
+#else
+ sprintf(buf, "vector: realloc() failed allocating %u bytes", (uint32_t)desired_size);
+#endif
+ fprintf(stderr, "%s", buf);
+ abort();
+ }
+
+#if BASISU_VECTOR_DETERMINISTIC
+ actual_size = desired_size;
+#elif defined(_MSC_VER)
+ actual_size = _msize(new_p);
+#elif HAS_MALLOC_USABLE_SIZE
+ actual_size = malloc_usable_size(new_p);
+#else
+ actual_size = desired_size;
+#endif
+ m_p = new_p;
+ }
+ else
+ {
+ void* new_p = malloc(desired_size);
+ if (!new_p)
+ {
+ if (nofail)
+ return false;
+
+ char buf[256];
+#ifdef _MSC_VER
+ sprintf_s(buf, sizeof(buf), "vector: malloc() failed allocating %u bytes", (uint32_t)desired_size);
+#else
+ sprintf(buf, "vector: malloc() failed allocating %u bytes", (uint32_t)desired_size);
+#endif
+ fprintf(stderr, "%s", buf);
+ abort();
+ }
+
+#if BASISU_VECTOR_DETERMINISTIC
+ actual_size = desired_size;
+#elif defined(_MSC_VER)
+ actual_size = _msize(new_p);
+#elif HAS_MALLOC_USABLE_SIZE
+ actual_size = malloc_usable_size(new_p);
+#else
+ actual_size = desired_size;
+#endif
+
+ (*pMover)(new_p, m_p, m_size);
+
+ if (m_p)
+ free(m_p);
+
+ m_p = new_p;
+ }
+
+ if (actual_size > desired_size)
+ m_capacity = static_cast(actual_size / element_size);
+ else
+ m_capacity = static_cast(new_capacity);
+
+ return true;
+ }
+
+#if BASISU_HASHMAP_TEST
+
+#define HASHMAP_TEST_VERIFY(c) do { if (!(c)) handle_hashmap_test_verify_failure(__LINE__); } while(0)
+
+ static void handle_hashmap_test_verify_failure(int line)
+ {
+ fprintf(stderr, "HASHMAP_TEST_VERIFY() faild on line %i\n", line);
+ abort();
+ }
+
+ class counted_obj
+ {
+ public:
+ counted_obj(uint32_t v = 0) :
+ m_val(v)
+ {
+ m_count++;
+ }
+
+ counted_obj(const counted_obj& obj) :
+ m_val(obj.m_val)
+ {
+ m_count++;
+ }
+
+ ~counted_obj()
+ {
+ assert(m_count > 0);
+ m_count--;
+ }
+
+ static uint32_t m_count;
+
+ uint32_t m_val;
+
+ operator size_t() const { return m_val; }
+
+ bool operator== (const counted_obj& rhs) const { return m_val == rhs.m_val; }
+ bool operator== (const uint32_t rhs) const { return m_val == rhs; }
+
+ };
+
+ uint32_t counted_obj::m_count;
+
+ static uint32_t urand32()
+ {
+ uint32_t a = rand();
+ uint32_t b = rand() << 15;
+ uint32_t c = rand() << (32 - 15);
+ return a ^ b ^ c;
+ }
+
+ static int irand32(int l, int h)
+ {
+ assert(l < h);
+ if (l >= h)
+ return l;
+
+ uint32_t range = static_cast(h - l);
+
+ uint32_t rnd = urand32();
+
+ uint32_t rnd_range = static_cast((((uint64_t)range) * ((uint64_t)rnd)) >> 32U);
+
+ int result = l + rnd_range;
+ assert((result >= l) && (result < h));
+ return result;
+ }
+
+ void hash_map_test()
+ {
+ {
+ basisu::hash_map k;
+ basisu::hash_map l;
+ std::swap(k, l);
+
+ k.begin();
+ k.end();
+ k.clear();
+ k.empty();
+ k.erase(0);
+ k.insert(0, 1);
+ k.find(0);
+ k.get_equals();
+ k.get_hasher();
+ k.get_table_size();
+ k.reset();
+ k.reserve(1);
+ k = l;
+ k.set_equals(l.get_equals());
+ k.set_hasher(l.get_hasher());
+ k.get_table_size();
+ }
+
+ uint32_t seed = 0;
+ for (; ; )
+ {
+ seed++;
+
+ typedef basisu::hash_map my_hash_map;
+ my_hash_map m;
+
+ const uint32_t n = irand32(0, 100000);
+
+ printf("%u\n", n);
+
+ srand(seed); // r1.seed(seed);
+
+ basisu::vector q;
+
+ uint32_t count = 0;
+ for (uint32_t i = 0; i < n; i++)
+ {
+ uint32_t v = urand32() & 0x7FFFFFFF;
+ my_hash_map::insert_result res = m.insert(counted_obj(v), counted_obj(v ^ 0xdeadbeef));
+ if (res.second)
+ {
+ count++;
+ q.push_back(v);
+ }
+ }
+
+ HASHMAP_TEST_VERIFY(m.size() == count);
+
+ srand(seed);
+
+ my_hash_map cm(m);
+ m.clear();
+ m = cm;
+ cm.reset();
+
+ for (uint32_t i = 0; i < n; i++)
+ {
+ uint32_t v = urand32() & 0x7FFFFFFF;
+ my_hash_map::const_iterator it = m.find(counted_obj(v));
+ HASHMAP_TEST_VERIFY(it != m.end());
+ HASHMAP_TEST_VERIFY(it->first == v);
+ HASHMAP_TEST_VERIFY(it->second == (v ^ 0xdeadbeef));
+ }
+
+ for (uint32_t t = 0; t < 2; t++)
+ {
+ const uint32_t nd = irand32(1, q.size() + 1);
+ for (uint32_t i = 0; i < nd; i++)
+ {
+ uint32_t p = irand32(0, q.size());
+
+ int k = q[p];
+ if (k >= 0)
+ {
+ q[p] = -k - 1;
+
+ bool s = m.erase(counted_obj(k));
+ HASHMAP_TEST_VERIFY(s);
+ }
+ }
+
+ typedef basisu::hash_map uint_hash_set;
+ uint_hash_set s;
+
+ for (uint32_t i = 0; i < q.size(); i++)
+ {
+ int v = q[i];
+
+ if (v >= 0)
+ {
+ my_hash_map::const_iterator it = m.find(counted_obj(v));
+ HASHMAP_TEST_VERIFY(it != m.end());
+ HASHMAP_TEST_VERIFY(it->first == (uint32_t)v);
+ HASHMAP_TEST_VERIFY(it->second == ((uint32_t)v ^ 0xdeadbeef));
+
+ s.insert(v);
+ }
+ else
+ {
+ my_hash_map::const_iterator it = m.find(counted_obj(-v - 1));
+ HASHMAP_TEST_VERIFY(it == m.end());
+ }
+ }
+
+ uint32_t found_count = 0;
+ for (my_hash_map::const_iterator it = m.begin(); it != m.end(); ++it)
+ {
+ HASHMAP_TEST_VERIFY(it->second == ((uint32_t)it->first ^ 0xdeadbeef));
+
+ uint_hash_set::const_iterator fit(s.find((uint32_t)it->first));
+ HASHMAP_TEST_VERIFY(fit != s.end());
+
+ HASHMAP_TEST_VERIFY(fit->first == it->first);
+
+ found_count++;
+ }
+
+ HASHMAP_TEST_VERIFY(found_count == s.size());
+ }
+
+ HASHMAP_TEST_VERIFY(counted_obj::m_count == m.size() * 2);
+ }
+ }
+
+#endif // BASISU_HASHMAP_TEST
+
+} // namespace basisu
diff --git a/ext/basis_universal/basisu_file_headers.h b/ext/basis_universal/basisu_file_headers.h
new file mode 100644
index 0000000000..4316d738e6
--- /dev/null
+++ b/ext/basis_universal/basisu_file_headers.h
@@ -0,0 +1,142 @@
+// basis_file_headers.h
+// Copyright (C) 2019-2020 Binomial LLC. All Rights Reserved.
+//
+// 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 "basisu_transcoder_internal.h"
+
+namespace basist
+{
+ // Slice desc header flags
+ enum basis_slice_desc_flags
+ {
+ cSliceDescFlagsHasAlpha = 1,
+
+ // Video only: Frame doesn't refer to previous frame (no usage of conditional replenishment pred symbols)
+ // Currently the first frame is always an I-Frame, all subsequent frames are P-Frames. This will eventually be changed to periodic I-Frames.
+ cSliceDescFlagsFrameIsIFrame = 2
+ };
+
+#pragma pack(push)
+#pragma pack(1)
+ struct basis_slice_desc
+ {
+ basisu::packed_uint<3> m_image_index; // The index of the source image provided to the encoder (will always appear in order from first to last, first image index is 0, no skipping allowed)
+ basisu::packed_uint<1> m_level_index; // The mipmap level index (mipmaps will always appear from largest to smallest)
+ basisu::packed_uint<1> m_flags; // enum basis_slice_desc_flags
+
+ basisu::packed_uint<2> m_orig_width; // The original image width (may not be a multiple of 4 pixels)
+ basisu::packed_uint<2> m_orig_height; // The original image height (may not be a multiple of 4 pixels)
+
+ basisu::packed_uint<2> m_num_blocks_x; // The slice's block X dimensions. Each block is 4x4 pixels. The slice's pixel resolution may or may not be a power of 2.
+ basisu::packed_uint<2> m_num_blocks_y; // The slice's block Y dimensions.
+
+ basisu::packed_uint<4> m_file_ofs; // Offset from the start of the file to the start of the slice's data
+ basisu::packed_uint<4> m_file_size; // The size of the compressed slice data in bytes
+
+ basisu::packed_uint<2> m_slice_data_crc16; // The CRC16 of the compressed slice data, for extra-paranoid use cases
+ };
+
+ // File header files
+ enum basis_header_flags
+ {
+ // Always set for ETC1S files. Not set for UASTC files.
+ cBASISHeaderFlagETC1S = 1,
+
+ // Set if the texture had to be Y flipped before encoding. The actual interpretation of this (is Y up or down?) is up to the user.
+ cBASISHeaderFlagYFlipped = 2,
+
+ // Set if any slices contain alpha (for ETC1S, if the odd slices contain alpha data)
+ cBASISHeaderFlagHasAlphaSlices = 4,
+
+ // For ETC1S files, this will be true if the file utilizes a codebook from another .basis file.
+ cBASISHeaderFlagUsesGlobalCodebook = 8,
+
+ // Set if the texture data is sRGB, otherwise it's linear.
+ // In reality, we have no idea if the texture data is actually linear or sRGB. This is the m_perceptual parameter passed to the compressor.
+ cBASISHeaderFlagSRGB = 16,
+ };
+
+ // The image type field attempts to describe how to interpret the image data in a Basis file.
+ // The encoder library doesn't really do anything special or different with these texture types, this is mostly here for the benefit of the user.
+ // We do make sure the various constraints are followed (2DArray/cubemap/videoframes/volume implies that each image has the same resolution and # of mipmap levels, etc., cubemap implies that the # of image slices is a multiple of 6)
+ enum basis_texture_type
+ {
+ cBASISTexType2D = 0, // An arbitrary array of 2D RGB or RGBA images with optional mipmaps, array size = # images, each image may have a different resolution and # of mipmap levels
+ cBASISTexType2DArray = 1, // An array of 2D RGB or RGBA images with optional mipmaps, array size = # images, each image has the same resolution and mipmap levels
+ cBASISTexTypeCubemapArray = 2, // an array of cubemap levels, total # of images must be divisable by 6, in X+, X-, Y+, Y-, Z+, Z- order, with optional mipmaps
+ cBASISTexTypeVideoFrames = 3, // An array of 2D video frames, with optional mipmaps, # frames = # images, each image has the same resolution and # of mipmap levels
+ cBASISTexTypeVolume = 4, // A 3D texture with optional mipmaps, Z dimension = # images, each image has the same resolution and # of mipmap levels
+
+ cBASISTexTypeTotal
+ };
+
+ enum
+ {
+ cBASISMaxUSPerFrame = 0xFFFFFF
+ };
+
+ enum class basis_tex_format
+ {
+ cETC1S = 0,
+ cUASTC4x4 = 1
+ };
+
+ struct basis_file_header
+ {
+ enum
+ {
+ cBASISSigValue = ('B' << 8) | 's',
+ cBASISFirstVersion = 0x10
+ };
+
+ basisu::packed_uint<2> m_sig; // 2 byte file signature
+ basisu::packed_uint<2> m_ver; // Baseline file version
+ basisu::packed_uint<2> m_header_size; // Header size in bytes, sizeof(basis_file_header)
+ basisu::packed_uint<2> m_header_crc16; // CRC16 of the remaining header data
+
+ basisu::packed_uint<4> m_data_size; // The total size of all data after the header
+ basisu::packed_uint<2> m_data_crc16; // The CRC16 of all data after the header
+
+ basisu::packed_uint<3> m_total_slices; // The total # of compressed slices (1 slice per image, or 2 for alpha .basis files)
+
+ basisu::packed_uint<3> m_total_images; // The total # of images
+
+ basisu::packed_uint<1> m_tex_format; // enum basis_tex_format
+ basisu::packed_uint<2> m_flags; // enum basist::header_flags
+ basisu::packed_uint<1> m_tex_type; // enum basist::basis_texture_type
+ basisu::packed_uint<3> m_us_per_frame; // Framerate of video, in microseconds per frame
+
+ basisu::packed_uint<4> m_reserved; // For future use
+ basisu::packed_uint<4> m_userdata0; // For client use
+ basisu::packed_uint<4> m_userdata1; // For client use
+
+ basisu::packed_uint<2> m_total_endpoints; // The number of endpoints in the endpoint codebook
+ basisu::packed_uint<4> m_endpoint_cb_file_ofs; // The compressed endpoint codebook's file offset relative to the start of the file
+ basisu::packed_uint<3> m_endpoint_cb_file_size; // The compressed endpoint codebook's size in bytes
+
+ basisu::packed_uint<2> m_total_selectors; // The number of selectors in the endpoint codebook
+ basisu::packed_uint<4> m_selector_cb_file_ofs; // The compressed selectors codebook's file offset relative to the start of the file
+ basisu::packed_uint<3> m_selector_cb_file_size; // The compressed selector codebook's size in bytes
+
+ basisu::packed_uint<4> m_tables_file_ofs; // The file offset of the compressed Huffman codelength tables, for decompressing slices
+ basisu::packed_uint<4> m_tables_file_size; // The file size in bytes of the compressed huffman codelength tables
+
+ basisu::packed_uint<4> m_slice_desc_file_ofs; // The file offset to the slice description array, usually follows the header
+
+ basisu::packed_uint<4> m_extended_file_ofs; // The file offset of the "extended" header and compressed data, for future use
+ basisu::packed_uint<4> m_extended_file_size; // The file size in bytes of the "extended" header and compressed data, for future use
+ };
+#pragma pack (pop)
+
+} // namespace basist
diff --git a/ext/basis_universal/basisu_transcoder.cpp b/ext/basis_universal/basisu_transcoder.cpp
new file mode 100644
index 0000000000..b5bef83c29
--- /dev/null
+++ b/ext/basis_universal/basisu_transcoder.cpp
@@ -0,0 +1,17479 @@
+// basisu_transcoder.cpp
+// Copyright (C) 2019-2021 Binomial LLC. All Rights Reserved.
+//
+// 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 "basisu_transcoder.h"
+#include
+#include "basisu_containers_impl.h"
+
+#ifndef BASISD_IS_BIG_ENDIAN
+// TODO: This doesn't work on OSX. How can this be so difficult?
+//#if defined(__BIG_ENDIAN__) || defined(_BIG_ENDIAN) || defined(BIG_ENDIAN)
+// #define BASISD_IS_BIG_ENDIAN (1)
+//#else
+ #define BASISD_IS_BIG_ENDIAN (0)
+//#endif
+#endif
+
+#ifndef BASISD_USE_UNALIGNED_WORD_READS
+ #ifdef __EMSCRIPTEN__
+ // Can't use unaligned loads/stores with WebAssembly.
+ #define BASISD_USE_UNALIGNED_WORD_READS (0)
+ #elif defined(_M_AMD64) || defined(_M_IX86) || defined(__i386__) || defined(__x86_64__)
+ #define BASISD_USE_UNALIGNED_WORD_READS (1)
+ #else
+ #define BASISD_USE_UNALIGNED_WORD_READS (0)
+ #endif
+#endif
+
+// Using unaligned loads and stores causes errors when using UBSan. Jam it off.
+#if defined(__has_feature)
+#if __has_feature(undefined_behavior_sanitizer)
+#undef BASISD_USE_UNALIGNED_WORD_READS
+#define BASISD_USE_UNALIGNED_WORD_READS 0
+#endif
+#endif
+
+#define BASISD_SUPPORTED_BASIS_VERSION (0x13)
+
+#ifndef BASISD_SUPPORT_KTX2
+ #error Must have defined BASISD_SUPPORT_KTX2
+#endif
+
+#ifndef BASISD_SUPPORT_KTX2_ZSTD
+#error Must have defined BASISD_SUPPORT_KTX2_ZSTD
+#endif
+
+// Set to 1 for fuzz testing. This will disable all CRC16 checks on headers and compressed data.
+#ifndef BASISU_NO_HEADER_OR_DATA_CRC16_CHECKS
+ #define BASISU_NO_HEADER_OR_DATA_CRC16_CHECKS 0
+#endif
+
+#ifndef BASISD_SUPPORT_DXT1
+ #define BASISD_SUPPORT_DXT1 1
+#endif
+
+#ifndef BASISD_SUPPORT_DXT5A
+ #define BASISD_SUPPORT_DXT5A 1
+#endif
+
+// Disable all BC7 transcoders if necessary (useful when cross compiling to Javascript)
+#if defined(BASISD_SUPPORT_BC7) && !BASISD_SUPPORT_BC7
+ #ifndef BASISD_SUPPORT_BC7_MODE5
+ #define BASISD_SUPPORT_BC7_MODE5 0
+ #endif
+#endif // !BASISD_SUPPORT_BC7
+
+// BC7 mode 5 supports both opaque and opaque+alpha textures, and uses less memory BC1.
+#ifndef BASISD_SUPPORT_BC7_MODE5
+ #define BASISD_SUPPORT_BC7_MODE5 1
+#endif
+
+#ifndef BASISD_SUPPORT_PVRTC1
+ #define BASISD_SUPPORT_PVRTC1 1
+#endif
+
+#ifndef BASISD_SUPPORT_ETC2_EAC_A8
+ #define BASISD_SUPPORT_ETC2_EAC_A8 1
+#endif
+
+// Set BASISD_SUPPORT_UASTC to 0 to completely disable support for transcoding UASTC files.
+#ifndef BASISD_SUPPORT_UASTC
+ #define BASISD_SUPPORT_UASTC 1
+#endif
+
+#ifndef BASISD_SUPPORT_ASTC
+ #define BASISD_SUPPORT_ASTC 1
+#endif
+
+// Note that if BASISD_SUPPORT_ATC is enabled, BASISD_SUPPORT_DXT5A should also be enabled for alpha support.
+#ifndef BASISD_SUPPORT_ATC
+ #define BASISD_SUPPORT_ATC 1
+#endif
+
+// Support for ETC2 EAC R11 and ETC2 EAC RG11
+#ifndef BASISD_SUPPORT_ETC2_EAC_RG11
+ #define BASISD_SUPPORT_ETC2_EAC_RG11 1
+#endif
+
+// If BASISD_SUPPORT_ASTC_HIGHER_OPAQUE_QUALITY is 1, opaque blocks will be transcoded to ASTC at slightly higher quality (higher than BC1), but the transcoder tables will be 2x as large.
+// This impacts grayscale and grayscale+alpha textures the most.
+#ifndef BASISD_SUPPORT_ASTC_HIGHER_OPAQUE_QUALITY
+ #ifdef __EMSCRIPTEN__
+ // Let's assume size matters more than quality when compiling with emscripten.
+ #define BASISD_SUPPORT_ASTC_HIGHER_OPAQUE_QUALITY 0
+ #else
+ // Compiling native, so an extra 64K lookup table is probably acceptable.
+ #define BASISD_SUPPORT_ASTC_HIGHER_OPAQUE_QUALITY 1
+ #endif
+#endif
+
+#ifndef BASISD_SUPPORT_FXT1
+ #define BASISD_SUPPORT_FXT1 1
+#endif
+
+#ifndef BASISD_SUPPORT_PVRTC2
+ #define BASISD_SUPPORT_PVRTC2 1
+#endif
+
+#if BASISD_SUPPORT_PVRTC2
+ #if !BASISD_SUPPORT_ATC
+ #error BASISD_SUPPORT_ATC must be 1 if BASISD_SUPPORT_PVRTC2 is 1
+ #endif
+#endif
+
+#if BASISD_SUPPORT_ATC
+ #if !BASISD_SUPPORT_DXT5A
+ #error BASISD_SUPPORT_DXT5A must be 1 if BASISD_SUPPORT_ATC is 1
+ #endif
+#endif
+
+#define BASISD_WRITE_NEW_BC7_MODE5_TABLES 0
+#define BASISD_WRITE_NEW_DXT1_TABLES 0
+#define BASISD_WRITE_NEW_ETC2_EAC_A8_TABLES 0
+#define BASISD_WRITE_NEW_ASTC_TABLES 0
+#define BASISD_WRITE_NEW_ATC_TABLES 0
+#define BASISD_WRITE_NEW_ETC2_EAC_R11_TABLES 0
+
+#ifndef BASISD_ENABLE_DEBUG_FLAGS
+ #define BASISD_ENABLE_DEBUG_FLAGS 0
+#endif
+
+// If KTX2 support is enabled, we may need Zstd for decompression of supercompressed UASTC files. Include this header.
+#if BASISD_SUPPORT_KTX2
+ // If BASISD_SUPPORT_KTX2_ZSTD is 0, UASTC files compressed with Zstd cannot be loaded.
+ #if BASISD_SUPPORT_KTX2_ZSTD
+ // We only use two Zstd API's: ZSTD_decompress() and ZSTD_isError()
+ #include "../zstd/lib/zstd.h"
+ #endif
+#endif
+
+namespace basisu
+{
+ bool g_debug_printf;
+
+ void enable_debug_printf(bool enabled)
+ {
+ g_debug_printf = enabled;
+ }
+
+ void debug_printf(const char* pFmt, ...)
+ {
+#if BASISU_FORCE_DEVEL_MESSAGES
+ g_debug_printf = true;
+#endif
+ if (g_debug_printf)
+ {
+ va_list args;
+ va_start(args, pFmt);
+ vprintf(pFmt, args);
+ va_end(args);
+ }
+ }
+} // namespace basisu
+
+namespace basist
+{
+
+#if BASISD_ENABLE_DEBUG_FLAGS
+ static uint32_t g_debug_flags = 0;
+#endif
+
+ uint32_t get_debug_flags()
+ {
+#if BASISD_ENABLE_DEBUG_FLAGS
+ return g_debug_flags;
+#else
+ return 0;
+#endif
+ }
+
+ void set_debug_flags(uint32_t f)
+ {
+ BASISU_NOTE_UNUSED(f);
+#if BASISD_ENABLE_DEBUG_FLAGS
+ g_debug_flags = f;
+#endif
+ }
+
+ inline uint16_t byteswap_uint16(uint16_t v)
+ {
+ return static_cast((v >> 8) | (v << 8));
+ }
+
+ static inline int32_t clampi(int32_t value, int32_t low, int32_t high) { if (value < low) value = low; else if (value > high) value = high; return value; }
+ static inline float clampf(float value, float low, float high) { if (value < low) value = low; else if (value > high) value = high; return value; }
+ static inline float saturate(float value) { return clampf(value, 0, 1.0f); }
+
+ static inline uint8_t mul_8(uint32_t v, uint32_t q) { v = v * q + 128; return (uint8_t)((v + (v >> 8)) >> 8); }
+
+ uint16_t crc16(const void* r, size_t size, uint16_t crc)
+ {
+ crc = ~crc;
+
+ const uint8_t* p = static_cast(r);
+ for (; size; --size)
+ {
+ const uint16_t q = *p++ ^ (crc >> 8);
+ uint16_t k = (q >> 4) ^ q;
+ crc = (((crc << 8) ^ k) ^ (k << 5)) ^ (k << 12);
+ }
+
+ return static_cast(~crc);
+ }
+
+ 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
+ };
+
+#define DECLARE_ETC1_INTEN_TABLE(name, N) \
+ static const int name[cETC1IntenModifierValues][cETC1SelectorValues] = \
+ { \
+ { N * -8, N * -2, N * 2, N * 8 },{ N * -17, N * -5, N * 5, N * 17 },{ N * -29, N * -9, N * 9, N * 29 },{ N * -42, N * -13, N * 13, N * 42 }, \
+ { N * -60, N * -18, N * 18, N * 60 },{ N * -80, N * -24, N * 24, N * 80 },{ N * -106, N * -33, N * 33, N * 106 },{ N * -183, N * -47, N * 47, N * 183 } \
+ };
+
+ DECLARE_ETC1_INTEN_TABLE(g_etc1_inten_tables, 1);
+ DECLARE_ETC1_INTEN_TABLE(g_etc1_inten_tables16, 16);
+ DECLARE_ETC1_INTEN_TABLE(g_etc1_inten_tables48, 3 * 16);
+
+ //const uint8_t g_etc1_to_selector_index[cETC1SelectorValues] = { 2, 3, 1, 0 };
+ const uint8_t g_selector_index_to_etc1[cETC1SelectorValues] = { 3, 2, 0, 1 };
+
+ static const uint8_t g_etc_5_to_8[32] = { 0, 8, 16, 24, 33, 41, 49, 57, 66, 74, 82, 90, 99, 107, 115, 123, 132, 140, 148, 156, 165, 173, 181, 189, 198, 206, 214, 222, 231, 239, 247, 255 };
+
+ struct decoder_etc_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_t m_uint64;
+
+ uint32_t m_uint32[2];
+
+ uint8_t m_bytes[8];
+
+ struct
+ {
+ signed m_dred2 : 3;
+ uint32_t m_red1 : 5;
+
+ signed m_dgreen2 : 3;
+ uint32_t m_green1 : 5;
+
+ signed m_dblue2 : 3;
+ uint32_t m_blue1 : 5;
+
+ uint32_t m_flip : 1;
+ uint32_t m_diff : 1;
+ uint32_t m_cw2 : 3;
+ uint32_t m_cw1 : 3;
+
+ uint32_t m_selectors;
+ } m_differential;
+ };
+
+ inline void clear()
+ {
+ assert(sizeof(*this) == 8);
+ basisu::clear_obj(*this);
+ }
+
+ inline void set_byte_bits(uint32_t ofs, uint32_t num, uint32_t bits)
+ {
+ assert((ofs + num) <= 64U);
+ assert(num && (num < 32U));
+ assert((ofs >> 3) == ((ofs + num - 1) >> 3));
+ assert(bits < (1U << num));
+ const uint32_t byte_ofs = 7 - (ofs >> 3);
+ const uint32_t byte_bit_ofs = ofs & 7;
+ const uint32_t mask = (1 << num) - 1;
+ m_bytes[byte_ofs] &= ~(mask << byte_bit_ofs);
+ m_bytes[byte_ofs] |= (bits << byte_bit_ofs);
+ }
+
+ inline void set_flip_bit(bool flip)
+ {
+ m_bytes[3] &= ~1;
+ m_bytes[3] |= static_cast(flip);
+ }
+
+ inline void set_diff_bit(bool diff)
+ {
+ m_bytes[3] &= ~2;
+ m_bytes[3] |= (static_cast(diff) << 1);
+ }
+
+ // Sets intensity modifier table (0-7) used by subblock subblock_id (0 or 1)
+ inline void set_inten_table(uint32_t subblock_id, uint32_t t)
+ {
+ assert(subblock_id < 2);
+ assert(t < 8);
+ const uint32_t ofs = subblock_id ? 2 : 5;
+ m_bytes[3] &= ~(7 << ofs);
+ m_bytes[3] |= (t << ofs);
+ }
+
+ // Selector "val" ranges from 0-3 and is a direct index into g_etc1_inten_tables.
+ inline void set_selector(uint32_t x, uint32_t y, uint32_t val)
+ {
+ assert((x | y | val) < 4);
+ const uint32_t bit_index = x * 4 + y;
+
+ uint8_t* p = &m_bytes[7 - (bit_index >> 3)];
+
+ const uint32_t byte_bit_ofs = bit_index & 7;
+ const uint32_t mask = 1 << byte_bit_ofs;
+
+ static const uint8_t s_selector_index_to_etc1[4] = { 3, 2, 0, 1 };
+ const uint32_t etc1_val = s_selector_index_to_etc1[val];
+
+ const uint32_t lsb = etc1_val & 1;
+ const uint32_t msb = etc1_val >> 1;
+
+ p[0] &= ~mask;
+ p[0] |= (lsb << byte_bit_ofs);
+
+ p[-2] &= ~mask;
+ p[-2] |= (msb << byte_bit_ofs);
+ }
+
+ // Returned encoded selector value ranges from 0-3 (this is NOT a direct index into g_etc1_inten_tables, see get_selector())
+ inline uint32_t get_raw_selector(uint32_t x, uint32_t y) const
+ {
+ assert((x | y) < 4);
+
+ const uint32_t bit_index = x * 4 + y;
+ const uint32_t byte_bit_ofs = bit_index & 7;
+ const uint8_t* p = &m_bytes[7 - (bit_index >> 3)];
+ const uint32_t lsb = (p[0] >> byte_bit_ofs) & 1;
+ const uint32_t msb = (p[-2] >> byte_bit_ofs) & 1;
+ const uint32_t val = lsb | (msb << 1);
+
+ return val;
+ }
+
+ // Returned selector value ranges from 0-3 and is a direct index into g_etc1_inten_tables.
+ inline uint32_t get_selector(uint32_t x, uint32_t y) const
+ {
+ static const uint8_t s_etc1_to_selector_index[cETC1SelectorValues] = { 2, 3, 1, 0 };
+ return s_etc1_to_selector_index[get_raw_selector(x, y)];
+ }
+
+ inline void set_raw_selector_bits(uint32_t bits)
+ {
+ m_bytes[4] = static_cast(bits);
+ m_bytes[5] = static_cast(bits >> 8);
+ m_bytes[6] = static_cast(bits >> 16);
+ m_bytes[7] = static_cast(bits >> 24);
+ }
+
+ inline bool are_all_selectors_the_same() const
+ {
+ uint32_t v = *reinterpret_cast(&m_bytes[4]);
+
+ if ((v == 0xFFFFFFFF) || (v == 0xFFFF) || (!v) || (v == 0xFFFF0000))
+ return true;
+
+ return false;
+ }
+
+ inline void set_raw_selector_bits(uint8_t byte0, uint8_t byte1, uint8_t byte2, uint8_t byte3)
+ {
+ m_bytes[4] = byte0;
+ m_bytes[5] = byte1;
+ m_bytes[6] = byte2;
+ m_bytes[7] = byte3;
+ }
+
+ inline uint32_t get_raw_selector_bits() const
+ {
+ return m_bytes[4] | (m_bytes[5] << 8) | (m_bytes[6] << 16) | (m_bytes[7] << 24);
+ }
+
+ inline void set_base4_color(uint32_t idx, uint16_t 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 void set_base5_color(uint16_t c)
+ {
+ set_byte_bits(cETC1BaseColor5RBitOffset, 5, (c >> 10) & 31);
+ set_byte_bits(cETC1BaseColor5GBitOffset, 5, (c >> 5) & 31);
+ set_byte_bits(cETC1BaseColor5BBitOffset, 5, c & 31);
+ }
+
+ void set_delta3_color(uint16_t c)
+ {
+ set_byte_bits(cETC1DeltaColor3RBitOffset, 3, (c >> 6) & 7);
+ set_byte_bits(cETC1DeltaColor3GBitOffset, 3, (c >> 3) & 7);
+ set_byte_bits(cETC1DeltaColor3BBitOffset, 3, c & 7);
+ }
+
+ void set_block_color4(const color32& c0_unscaled, const color32& c1_unscaled)
+ {
+ set_diff_bit(false);
+
+ set_base4_color(0, pack_color4(c0_unscaled, false));
+ set_base4_color(1, pack_color4(c1_unscaled, false));
+ }
+
+ void set_block_color5(const color32& c0_unscaled, const color32& c1_unscaled)
+ {
+ set_diff_bit(true);
+
+ set_base5_color(pack_color5(c0_unscaled, false));
+
+ int dr = c1_unscaled.r - c0_unscaled.r;
+ int dg = c1_unscaled.g - c0_unscaled.g;
+ int db = c1_unscaled.b - c0_unscaled.b;
+
+ set_delta3_color(pack_delta3(dr, dg, db));
+ }
+
+ bool set_block_color5_check(const color32& c0_unscaled, const color32& c1_unscaled)
+ {
+ set_diff_bit(true);
+
+ set_base5_color(pack_color5(c0_unscaled, false));
+
+ int dr = c1_unscaled.r - c0_unscaled.r;
+ int dg = c1_unscaled.g - c0_unscaled.g;
+ int db = c1_unscaled.b - c0_unscaled.b;
+
+ if (((dr < cETC1ColorDeltaMin) || (dr > cETC1ColorDeltaMax)) ||
+ ((dg < cETC1ColorDeltaMin) || (dg > cETC1ColorDeltaMax)) ||
+ ((db < cETC1ColorDeltaMin) || (db > cETC1ColorDeltaMax)))
+ return false;
+
+ set_delta3_color(pack_delta3(dr, dg, db));
+
+ return true;
+ }
+
+ inline uint32_t get_byte_bits(uint32_t ofs, uint32_t num) const
+ {
+ assert((ofs + num) <= 64U);
+ assert(num && (num <= 8U));
+ assert((ofs >> 3) == ((ofs + num - 1) >> 3));
+ const uint32_t byte_ofs = 7 - (ofs >> 3);
+ const uint32_t byte_bit_ofs = ofs & 7;
+ return (m_bytes[byte_ofs] >> byte_bit_ofs) & ((1 << num) - 1);
+ }
+
+ inline uint16_t get_base5_color() const
+ {
+ const uint32_t r = get_byte_bits(cETC1BaseColor5RBitOffset, 5);
+ const uint32_t g = get_byte_bits(cETC1BaseColor5GBitOffset, 5);
+ const uint32_t b = get_byte_bits(cETC1BaseColor5BBitOffset, 5);
+ return static_cast(b | (g << 5U) | (r << 10U));
+ }
+
+ inline uint16_t get_base4_color(uint32_t idx) const
+ {
+ uint32_t 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(b | (g << 4U) | (r << 8U));
+ }
+
+ inline color32 get_base5_color_unscaled() const
+ {
+ return color32(m_differential.m_red1, m_differential.m_green1, m_differential.m_blue1, 255);
+ }
+
+ inline bool get_flip_bit() const
+ {
+ return (m_bytes[3] & 1) != 0;
+ }
+
+ inline bool get_diff_bit() const
+ {
+ return (m_bytes[3] & 2) != 0;
+ }
+
+ inline uint32_t get_inten_table(uint32_t subblock_id) const
+ {
+ assert(subblock_id < 2);
+ const uint32_t ofs = subblock_id ? 2 : 5;
+ return (m_bytes[3] >> ofs) & 7;
+ }
+
+ inline uint16_t get_delta3_color() const
+ {
+ const uint32_t r = get_byte_bits(cETC1DeltaColor3RBitOffset, 3);
+ const uint32_t g = get_byte_bits(cETC1DeltaColor3GBitOffset, 3);
+ const uint32_t b = get_byte_bits(cETC1DeltaColor3BBitOffset, 3);
+ return static_cast(b | (g << 3U) | (r << 6U));
+ }
+
+ void get_block_colors(color32* pBlock_colors, uint32_t subblock_index) const
+ {
+ color32 b;
+
+ if (get_diff_bit())
+ {
+ if (subblock_index)
+ unpack_color5(b, get_base5_color(), get_delta3_color(), true, 255);
+ else
+ unpack_color5(b, get_base5_color(), true);
+ }
+ else
+ {
+ b = unpack_color4(get_base4_color(subblock_index), true, 255);
+ }
+
+ const int* pInten_table = g_etc1_inten_tables[get_inten_table(subblock_index)];
+
+ pBlock_colors[0].set_noclamp_rgba(clamp255(b.r + pInten_table[0]), clamp255(b.g + pInten_table[0]), clamp255(b.b + pInten_table[0]), 255);
+ pBlock_colors[1].set_noclamp_rgba(clamp255(b.r + pInten_table[1]), clamp255(b.g + pInten_table[1]), clamp255(b.b + pInten_table[1]), 255);
+ pBlock_colors[2].set_noclamp_rgba(clamp255(b.r + pInten_table[2]), clamp255(b.g + pInten_table[2]), clamp255(b.b + pInten_table[2]), 255);
+ pBlock_colors[3].set_noclamp_rgba(clamp255(b.r + pInten_table[3]), clamp255(b.g + pInten_table[3]), clamp255(b.b + pInten_table[3]), 255);
+ }
+
+ static uint16_t pack_color4(const color32& color, bool scaled, uint32_t bias = 127U)
+ {
+ return pack_color4(color.r, color.g, color.b, scaled, bias);
+ }
+
+ static uint16_t pack_color4(uint32_t r, uint32_t g, uint32_t b, bool scaled, uint32_t bias = 127U)
+ {
+ if (scaled)
+ {
+ r = (r * 15U + bias) / 255U;
+ g = (g * 15U + bias) / 255U;
+ b = (b * 15U + bias) / 255U;
+ }
+
+ r = basisu::minimum(r, 15U);
+ g = basisu::minimum(g, 15U);
+ b = basisu::minimum(b, 15U);
+
+ return static_cast(b | (g << 4U) | (r << 8U));
+ }
+
+ static uint16_t pack_color5(const color32& color, bool scaled, uint32_t bias = 127U)
+ {
+ return pack_color5(color.r, color.g, color.b, scaled, bias);
+ }
+
+ static uint16_t pack_color5(uint32_t r, uint32_t g, uint32_t b, bool scaled, uint32_t bias = 127U)
+ {
+ if (scaled)
+ {
+ r = (r * 31U + bias) / 255U;
+ g = (g * 31U + bias) / 255U;
+ b = (b * 31U + bias) / 255U;
+ }
+
+ r = basisu::minimum(r, 31U);
+ g = basisu::minimum(g, 31U);
+ b = basisu::minimum(b, 31U);
+
+ return static_cast(b | (g << 5U) | (r << 10U));
+ }
+
+ uint16_t pack_delta3(const color32& color)
+ {
+ return pack_delta3(color.r, color.g, color.b);
+ }
+
+ uint16_t pack_delta3(int r, int g, int b)
+ {
+ assert((r >= cETC1ColorDeltaMin) && (r <= cETC1ColorDeltaMax));
+ assert((g >= cETC1ColorDeltaMin) && (g <= cETC1ColorDeltaMax));
+ assert((b >= cETC1ColorDeltaMin) && (b <= cETC1ColorDeltaMax));
+ if (r < 0) r += 8;
+ if (g < 0) g += 8;
+ if (b < 0) b += 8;
+ return static_cast(b | (g << 3) | (r << 6));
+ }
+
+ static void unpack_delta3(int& r, int& g, int& b, uint16_t 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;
+ }
+
+ static color32 unpack_color5(uint16_t packed_color5, bool scaled, uint32_t alpha)
+ {
+ uint32_t b = packed_color5 & 31U;
+ uint32_t g = (packed_color5 >> 5U) & 31U;
+ uint32_t r = (packed_color5 >> 10U) & 31U;
+
+ if (scaled)
+ {
+ b = (b << 3U) | (b >> 2U);
+ g = (g << 3U) | (g >> 2U);
+ r = (r << 3U) | (r >> 2U);
+ }
+
+ assert(alpha <= 255);
+
+ return color32(cNoClamp, r, g, b, alpha);
+ }
+
+ static void unpack_color5(uint32_t& r, uint32_t& g, uint32_t& b, uint16_t packed_color5, bool scaled)
+ {
+ color32 c(unpack_color5(packed_color5, scaled, 0));
+ r = c.r;
+ g = c.g;
+ b = c.b;
+ }
+
+ static void unpack_color5(color32& result, uint16_t packed_color5, bool scaled)
+ {
+ result = unpack_color5(packed_color5, scaled, 255);
+ }
+
+ static bool unpack_color5(color32& result, uint16_t packed_color5, uint16_t packed_delta3, bool scaled, uint32_t alpha)
+ {
+ int dr, dg, db;
+ unpack_delta3(dr, dg, db, packed_delta3);
+
+ int r = ((packed_color5 >> 10U) & 31U) + dr;
+ int g = ((packed_color5 >> 5U) & 31U) + dg;
+ int b = (packed_color5 & 31U) + db;
+
+ bool success = true;
+ if (static_cast(r | g | b) > 31U)
+ {
+ success = false;
+ r = basisu::clamp(r, 0, 31);
+ g = basisu::clamp(g, 0, 31);
+ b = basisu::clamp(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, basisu::minimum(alpha, 255U));
+ return success;
+ }
+
+ static color32 unpack_color4(uint16_t packed_color4, bool scaled, uint32_t alpha)
+ {
+ uint32_t b = packed_color4 & 15U;
+ uint32_t g = (packed_color4 >> 4U) & 15U;
+ uint32_t r = (packed_color4 >> 8U) & 15U;
+
+ if (scaled)
+ {
+ b = (b << 4U) | b;
+ g = (g << 4U) | g;
+ r = (r << 4U) | r;
+ }
+
+ return color32(cNoClamp, r, g, b, basisu::minimum(alpha, 255U));
+ }
+
+ static void unpack_color4(uint32_t& r, uint32_t& g, uint32_t& b, uint16_t packed_color4, bool scaled)
+ {
+ color32 c(unpack_color4(packed_color4, scaled, 0));
+ r = c.r;
+ g = c.g;
+ b = c.b;
+ }
+
+ static void get_diff_subblock_colors(color32* pDst, uint16_t packed_color5, uint32_t table_idx)
+ {
+ assert(table_idx < cETC1IntenModifierValues);
+ const int* pInten_modifer_table = &g_etc1_inten_tables[table_idx][0];
+
+ uint32_t r, g, b;
+ unpack_color5(r, g, b, packed_color5, true);
+
+ const int ir = static_cast(r), ig = static_cast(g), ib = static_cast(b);
+
+ const int y0 = pInten_modifer_table[0];
+ pDst[0].set(clamp255(ir + y0), clamp255(ig + y0), clamp255(ib + y0), 255);
+
+ const int y1 = pInten_modifer_table[1];
+ pDst[1].set(clamp255(ir + y1), clamp255(ig + y1), clamp255(ib + y1), 255);
+
+ const int y2 = pInten_modifer_table[2];
+ pDst[2].set(clamp255(ir + y2), clamp255(ig + y2), clamp255(ib + y2), 255);
+
+ const int y3 = pInten_modifer_table[3];
+ pDst[3].set(clamp255(ir + y3), clamp255(ig + y3), clamp255(ib + y3), 255);
+ }
+
+ static int clamp255(int x)
+ {
+ if (x & 0xFFFFFF00)
+ {
+ if (x < 0)
+ x = 0;
+ else if (x > 255)
+ x = 255;
+ }
+
+ return x;
+ }
+
+ static void get_block_colors5(color32* pBlock_colors, const color32& base_color5, uint32_t inten_table)
+ {
+ color32 b(base_color5);
+
+ b.r = (b.r << 3) | (b.r >> 2);
+ b.g = (b.g << 3) | (b.g >> 2);
+ b.b = (b.b << 3) | (b.b >> 2);
+
+ const int* pInten_table = g_etc1_inten_tables[inten_table];
+
+ pBlock_colors[0].set(clamp255(b.r + pInten_table[0]), clamp255(b.g + pInten_table[0]), clamp255(b.b + pInten_table[0]), 255);
+ pBlock_colors[1].set(clamp255(b.r + pInten_table[1]), clamp255(b.g + pInten_table[1]), clamp255(b.b + pInten_table[1]), 255);
+ pBlock_colors[2].set(clamp255(b.r + pInten_table[2]), clamp255(b.g + pInten_table[2]), clamp255(b.b + pInten_table[2]), 255);
+ pBlock_colors[3].set(clamp255(b.r + pInten_table[3]), clamp255(b.g + pInten_table[3]), clamp255(b.b + pInten_table[3]), 255);
+ }
+
+ static void get_block_color5(const color32& base_color5, uint32_t inten_table, uint32_t index, uint32_t& r, uint32_t &g, uint32_t &b)
+ {
+ assert(index < 4);
+
+ uint32_t br = (base_color5.r << 3) | (base_color5.r >> 2);
+ uint32_t bg = (base_color5.g << 3) | (base_color5.g >> 2);
+ uint32_t bb = (base_color5.b << 3) | (base_color5.b >> 2);
+
+ const int* pInten_table = g_etc1_inten_tables[inten_table];
+
+ r = clamp255(br + pInten_table[index]);
+ g = clamp255(bg + pInten_table[index]);
+ b = clamp255(bb + pInten_table[index]);
+ }
+
+ static void get_block_color5_r(const color32& base_color5, uint32_t inten_table, uint32_t index, uint32_t &r)
+ {
+ assert(index < 4);
+
+ uint32_t br = (base_color5.r << 3) | (base_color5.r >> 2);
+
+ const int* pInten_table = g_etc1_inten_tables[inten_table];
+
+ r = clamp255(br + pInten_table[index]);
+ }
+
+ static void get_block_colors5_g(int* pBlock_colors, const color32& base_color5, uint32_t inten_table)
+ {
+ const int g = (base_color5.g << 3) | (base_color5.g >> 2);
+
+ const int* pInten_table = g_etc1_inten_tables[inten_table];
+
+ pBlock_colors[0] = clamp255(g + pInten_table[0]);
+ pBlock_colors[1] = clamp255(g + pInten_table[1]);
+ pBlock_colors[2] = clamp255(g + pInten_table[2]);
+ pBlock_colors[3] = clamp255(g + pInten_table[3]);
+ }
+
+ static void get_block_colors5_bounds(color32* pBlock_colors, const color32& base_color5, uint32_t inten_table, uint32_t l = 0, uint32_t h = 3)
+ {
+ color32 b(base_color5);
+
+ b.r = (b.r << 3) | (b.r >> 2);
+ b.g = (b.g << 3) | (b.g >> 2);
+ b.b = (b.b << 3) | (b.b >> 2);
+
+ const int* pInten_table = g_etc1_inten_tables[inten_table];
+
+ pBlock_colors[0].set(clamp255(b.r + pInten_table[l]), clamp255(b.g + pInten_table[l]), clamp255(b.b + pInten_table[l]), 255);
+ pBlock_colors[1].set(clamp255(b.r + pInten_table[h]), clamp255(b.g + pInten_table[h]), clamp255(b.b + pInten_table[h]), 255);
+ }
+
+ static void get_block_colors5_bounds_g(uint32_t* pBlock_colors, const color32& base_color5, uint32_t inten_table, uint32_t l = 0, uint32_t h = 3)
+ {
+ color32 b(base_color5);
+
+ b.g = (b.g << 3) | (b.g >> 2);
+
+ const int* pInten_table = g_etc1_inten_tables[inten_table];
+
+ pBlock_colors[0] = clamp255(b.g + pInten_table[l]);
+ pBlock_colors[1] = clamp255(b.g + pInten_table[h]);
+ }
+ };
+
+ enum dxt_constants
+ {
+ cDXT1SelectorBits = 2U, cDXT1SelectorValues = 1U << cDXT1SelectorBits, cDXT1SelectorMask = cDXT1SelectorValues - 1U,
+ cDXT5SelectorBits = 3U, cDXT5SelectorValues = 1U << cDXT5SelectorBits, cDXT5SelectorMask = cDXT5SelectorValues - 1U,
+ };
+
+ static const uint8_t g_etc1_x_selector_unpack[4][256] =
+ {
+ {
+ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
+ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
+ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
+ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
+ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
+ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
+ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
+ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
+ },
+ {
+ 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1,
+ 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3,
+ 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1,
+ 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3,
+ 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1,
+ 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3,
+ 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1,
+ 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3,
+ },
+
+ {
+ 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1,
+ 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1,
+ 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3,
+ 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3,
+ 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1,
+ 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1,
+ 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3,
+ 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3,
+ },
+
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
+ 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
+ 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
+ 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
+ }
+ };
+
+ struct dxt1_block
+ {
+ enum { cTotalEndpointBytes = 2, cTotalSelectorBytes = 4 };
+
+ uint8_t m_low_color[cTotalEndpointBytes];
+ uint8_t m_high_color[cTotalEndpointBytes];
+ uint8_t m_selectors[cTotalSelectorBytes];
+
+ inline void clear() { basisu::clear_obj(*this); }
+
+ inline uint32_t get_high_color() const { return m_high_color[0] | (m_high_color[1] << 8U); }
+ inline uint32_t get_low_color() const { return m_low_color[0] | (m_low_color[1] << 8U); }
+ inline void set_low_color(uint16_t c) { m_low_color[0] = static_cast(c & 0xFF); m_low_color[1] = static_cast((c >> 8) & 0xFF); }
+ inline void set_high_color(uint16_t c) { m_high_color[0] = static_cast(c & 0xFF); m_high_color[1] = static_cast((c >> 8) & 0xFF); }
+ inline uint32_t get_selector(uint32_t x, uint32_t y) const { assert((x < 4U) && (y < 4U)); return (m_selectors[y] >> (x * cDXT1SelectorBits)) & cDXT1SelectorMask; }
+ inline void set_selector(uint32_t x, uint32_t y, uint32_t val) { assert((x < 4U) && (y < 4U) && (val < 4U)); m_selectors[y] &= (~(cDXT1SelectorMask << (x * cDXT1SelectorBits))); m_selectors[y] |= (val << (x * cDXT1SelectorBits)); }
+
+ static uint16_t pack_color(const color32& color, bool scaled, uint32_t bias = 127U)
+ {
+ uint32_t r = color.r, g = color.g, b = color.b;
+ if (scaled)
+ {
+ r = (r * 31U + bias) / 255U;
+ g = (g * 63U + bias) / 255U;
+ b = (b * 31U + bias) / 255U;
+ }
+ return static_cast(basisu::minimum(b, 31U) | (basisu::minimum(g, 63U) << 5U) | (basisu::minimum(r, 31U) << 11U));
+ }
+
+ static uint16_t pack_unscaled_color(uint32_t r, uint32_t g, uint32_t b) { return static_cast(b | (g << 5U) | (r << 11U)); }
+ };
+
+ struct dxt_selector_range
+ {
+ uint32_t m_low;
+ uint32_t m_high;
+ };
+
+ struct etc1_to_dxt1_56_solution
+ {
+ uint8_t m_lo;
+ uint8_t m_hi;
+ uint16_t m_err;
+ };
+
+#if BASISD_SUPPORT_DXT1
+ static dxt_selector_range g_etc1_to_dxt1_selector_ranges[] =
+ {
+ { 0, 3 },
+
+ { 1, 3 },
+ { 0, 2 },
+
+ { 1, 2 },
+
+ { 2, 3 },
+ { 0, 1 },
+ };
+
+ const uint32_t NUM_ETC1_TO_DXT1_SELECTOR_RANGES = sizeof(g_etc1_to_dxt1_selector_ranges) / sizeof(g_etc1_to_dxt1_selector_ranges[0]);
+
+ static uint32_t g_etc1_to_dxt1_selector_range_index[4][4];
+
+ const uint32_t NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS = 10;
+ static const uint8_t g_etc1_to_dxt1_selector_mappings[NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS][4] =
+ {
+ { 0, 0, 1, 1 },
+ { 0, 0, 1, 2 },
+ { 0, 0, 1, 3 },
+ { 0, 0, 2, 3 },
+ { 0, 1, 1, 1 },
+ { 0, 1, 2, 2 },
+ { 0, 1, 2, 3 },
+ { 0, 2, 3, 3 },
+ { 1, 2, 2, 2 },
+ { 1, 2, 3, 3 },
+ };
+
+ static uint8_t g_etc1_to_dxt1_selector_mappings_raw_dxt1_256[NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS][256];
+ static uint8_t g_etc1_to_dxt1_selector_mappings_raw_dxt1_inv_256[NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS][256];
+
+ static const etc1_to_dxt1_56_solution g_etc1_to_dxt_6[32 * 8 * NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS * NUM_ETC1_TO_DXT1_SELECTOR_RANGES] = {
+#include "basisu_transcoder_tables_dxt1_6.inc"
+ };
+
+ static const etc1_to_dxt1_56_solution g_etc1_to_dxt_5[32 * 8 * NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS * NUM_ETC1_TO_DXT1_SELECTOR_RANGES] = {
+#include "basisu_transcoder_tables_dxt1_5.inc"
+ };
+#endif // BASISD_SUPPORT_DXT1
+
+#if BASISD_SUPPORT_DXT1 || BASISD_SUPPORT_UASTC
+ // First saw the idea for optimal BC1 single-color block encoding using lookup tables in ryg_dxt.
+ struct bc1_match_entry
+ {
+ uint8_t m_hi;
+ uint8_t m_lo;
+ };
+ static bc1_match_entry g_bc1_match5_equals_1[256], g_bc1_match6_equals_1[256]; // selector 1, allow equals hi/lo
+ static bc1_match_entry g_bc1_match5_equals_0[256], g_bc1_match6_equals_0[256]; // selector 0, allow equals hi/lo
+
+ static void prepare_bc1_single_color_table(bc1_match_entry* pTable, const uint8_t* pExpand, int size0, int size1, int sel)
+ {
+ for (int i = 0; i < 256; i++)
+ {
+ int lowest_e = 256;
+ for (int lo = 0; lo < size0; lo++)
+ {
+ for (int hi = 0; hi < size1; hi++)
+ {
+ const int lo_e = pExpand[lo], hi_e = pExpand[hi];
+ int e;
+
+ if (sel == 1)
+ {
+ // Selector 1
+ e = basisu::iabs(((hi_e * 2 + lo_e) / 3) - i);
+ e += (basisu::iabs(hi_e - lo_e) * 3) / 100;
+ }
+ else
+ {
+ assert(sel == 0);
+
+ // Selector 0
+ e = basisu::iabs(hi_e - i);
+ }
+
+ if (e < lowest_e)
+ {
+ pTable[i].m_hi = static_cast(hi);
+ pTable[i].m_lo = static_cast(lo);
+
+ lowest_e = e;
+ }
+
+ } // hi
+ } // lo
+ }
+ }
+#endif
+
+#if BASISD_WRITE_NEW_DXT1_TABLES
+ static void create_etc1_to_dxt1_5_conversion_table()
+ {
+ FILE* pFile = nullptr;
+ fopen_s(&pFile, "basisu_transcoder_tables_dxt1_5.inc", "w");
+
+ uint32_t n = 0;
+
+ for (int inten = 0; inten < 8; inten++)
+ {
+ for (uint32_t g = 0; g < 32; g++)
+ {
+ color32 block_colors[4];
+ decoder_etc_block::get_diff_subblock_colors(block_colors, decoder_etc_block::pack_color5(color32(g, g, g, 255), false), inten);
+
+ for (uint32_t sr = 0; sr < NUM_ETC1_TO_DXT1_SELECTOR_RANGES; sr++)
+ {
+ const uint32_t low_selector = g_etc1_to_dxt1_selector_ranges[sr].m_low;
+ const uint32_t high_selector = g_etc1_to_dxt1_selector_ranges[sr].m_high;
+
+ for (uint32_t m = 0; m < NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS; m++)
+ {
+ uint32_t best_lo = 0;
+ uint32_t best_hi = 0;
+ uint64_t best_err = UINT64_MAX;
+
+ for (uint32_t hi = 0; hi <= 31; hi++)
+ {
+ for (uint32_t lo = 0; lo <= 31; lo++)
+ {
+ //if (lo == hi) continue;
+
+ uint32_t colors[4];
+
+ colors[0] = (lo << 3) | (lo >> 2);
+ colors[3] = (hi << 3) | (hi >> 2);
+
+ colors[1] = (colors[0] * 2 + colors[3]) / 3;
+ colors[2] = (colors[3] * 2 + colors[0]) / 3;
+
+ uint64_t total_err = 0;
+
+ for (uint32_t s = low_selector; s <= high_selector; s++)
+ {
+ int err = block_colors[s].g - colors[g_etc1_to_dxt1_selector_mappings[m][s]];
+
+ total_err += err * err;
+ }
+
+ if (total_err < best_err)
+ {
+ best_err = total_err;
+ best_lo = lo;
+ best_hi = hi;
+ }
+ }
+ }
+
+ assert(best_err <= 0xFFFF);
+
+ //table[g + inten * 32].m_solutions[sr][m].m_lo = static_cast(best_lo);
+ //table[g + inten * 32].m_solutions[sr][m].m_hi = static_cast(best_hi);
+ //table[g + inten * 32].m_solutions[sr][m].m_err = static_cast(best_err);
+
+ //assert(best_lo != best_hi);
+ fprintf(pFile, "{%u,%u,%u},", best_lo, best_hi, (uint32_t)best_err);
+ n++;
+ if ((n & 31) == 31)
+ fprintf(pFile, "\n");
+ } // m
+ } // sr
+ } // g
+ } // inten
+
+ fclose(pFile);
+ }
+
+ static void create_etc1_to_dxt1_6_conversion_table()
+ {
+ FILE* pFile = nullptr;
+ fopen_s(&pFile, "basisu_transcoder_tables_dxt1_6.inc", "w");
+
+ uint32_t n = 0;
+
+ for (int inten = 0; inten < 8; inten++)
+ {
+ for (uint32_t g = 0; g < 32; g++)
+ {
+ color32 block_colors[4];
+ decoder_etc_block::get_diff_subblock_colors(block_colors, decoder_etc_block::pack_color5(color32(g, g, g, 255), false), inten);
+
+ for (uint32_t sr = 0; sr < NUM_ETC1_TO_DXT1_SELECTOR_RANGES; sr++)
+ {
+ const uint32_t low_selector = g_etc1_to_dxt1_selector_ranges[sr].m_low;
+ const uint32_t high_selector = g_etc1_to_dxt1_selector_ranges[sr].m_high;
+
+ for (uint32_t m = 0; m < NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS; m++)
+ {
+ uint32_t best_lo = 0;
+ uint32_t best_hi = 0;
+ uint64_t best_err = UINT64_MAX;
+
+ for (uint32_t hi = 0; hi <= 63; hi++)
+ {
+ for (uint32_t lo = 0; lo <= 63; lo++)
+ {
+ //if (lo == hi) continue;
+
+ uint32_t colors[4];
+
+ colors[0] = (lo << 2) | (lo >> 4);
+ colors[3] = (hi << 2) | (hi >> 4);
+
+ colors[1] = (colors[0] * 2 + colors[3]) / 3;
+ colors[2] = (colors[3] * 2 + colors[0]) / 3;
+
+ uint64_t total_err = 0;
+
+ for (uint32_t s = low_selector; s <= high_selector; s++)
+ {
+ int err = block_colors[s].g - colors[g_etc1_to_dxt1_selector_mappings[m][s]];
+
+ total_err += err * err;
+ }
+
+ if (total_err < best_err)
+ {
+ best_err = total_err;
+ best_lo = lo;
+ best_hi = hi;
+ }
+ }
+ }
+
+ assert(best_err <= 0xFFFF);
+
+ //table[g + inten * 32].m_solutions[sr][m].m_lo = static_cast(best_lo);
+ //table[g + inten * 32].m_solutions[sr][m].m_hi = static_cast(best_hi);
+ //table[g + inten * 32].m_solutions[sr][m].m_err = static_cast(best_err);
+
+ //assert(best_lo != best_hi);
+ fprintf(pFile, "{%u,%u,%u},", best_lo, best_hi, (uint32_t)best_err);
+ n++;
+ if ((n & 31) == 31)
+ fprintf(pFile, "\n");
+
+ } // m
+ } // sr
+ } // g
+ } // inten
+
+ fclose(pFile);
+ }
+#endif
+
+
+#if BASISD_SUPPORT_UASTC || BASISD_SUPPORT_ETC2_EAC_A8 || BASISD_SUPPORT_ETC2_EAC_RG11
+ static const int8_t g_eac_modifier_table[16][8] =
+ {
+ { -3, -6, -9, -15, 2, 5, 8, 14 },
+ { -3, -7, -10, -13, 2, 6, 9, 12 },
+ { -2, -5, -8, -13, 1, 4, 7, 12 },
+ { -2, -4, -6, -13, 1, 3, 5, 12 },
+ { -3, -6, -8, -12, 2, 5, 7, 11 },
+ { -3, -7, -9, -11, 2, 6, 8, 10 },
+ { -4, -7, -8, -11, 3, 6, 7, 10 },
+ { -3, -5, -8, -11, 2, 4, 7, 10 },
+
+ { -2, -6, -8, -10, 1, 5, 7, 9 },
+ { -2, -5, -8, -10, 1, 4, 7, 9 },
+ { -2, -4, -8, -10, 1, 3, 7, 9 },
+ { -2, -5, -7, -10, 1, 4, 6, 9 },
+ { -3, -4, -7, -10, 2, 3, 6, 9 },
+ { -1, -2, -3, -10, 0, 1, 2, 9 }, // entry 13
+ { -4, -6, -8, -9, 3, 5, 7, 8 },
+ { -3, -5, -7, -9, 2, 4, 6, 8 }
+ };
+
+ // Used by ETC2 EAC A8 and ETC2 EAC R11/RG11.
+ struct eac_block
+ {
+ uint16_t m_base : 8;
+
+ uint16_t m_table : 4;
+ uint16_t m_multiplier : 4;
+
+ uint8_t m_selectors[6];
+
+ uint32_t get_selector(uint32_t x, uint32_t y) const
+ {
+ assert((x < 4) && (y < 4));
+
+ const uint32_t ofs = 45 - (y + x * 4) * 3;
+
+ const uint64_t pixels = get_selector_bits();
+
+ return (pixels >> ofs) & 7;
+ }
+
+ void set_selector(uint32_t x, uint32_t y, uint32_t s)
+ {
+ assert((x < 4) && (y < 4) && (s < 8));
+
+ const uint32_t ofs = 45 - (y + x * 4) * 3;
+
+ uint64_t pixels = get_selector_bits();
+
+ pixels &= ~(7ULL << ofs);
+ pixels |= (static_cast(s) << ofs);
+
+ set_selector_bits(pixels);
+ }
+
+ uint64_t get_selector_bits() const
+ {
+ uint64_t pixels = ((uint64_t)m_selectors[0] << 40) | ((uint64_t)m_selectors[1] << 32) |
+ ((uint64_t)m_selectors[2] << 24) |
+ ((uint64_t)m_selectors[3] << 16) | ((uint64_t)m_selectors[4] << 8) | m_selectors[5];
+ return pixels;
+ }
+
+ void set_selector_bits(uint64_t pixels)
+ {
+ m_selectors[0] = (uint8_t)(pixels >> 40);
+ m_selectors[1] = (uint8_t)(pixels >> 32);
+ m_selectors[2] = (uint8_t)(pixels >> 24);
+ m_selectors[3] = (uint8_t)(pixels >> 16);
+ m_selectors[4] = (uint8_t)(pixels >> 8);
+ m_selectors[5] = (uint8_t)(pixels);
+ }
+ };
+
+#endif // #if BASISD_SUPPORT_UASTC BASISD_SUPPORT_ETC2_EAC_A8 || BASISD_SUPPORT_ETC2_EAC_RG11
+
+#if BASISD_SUPPORT_ETC2_EAC_A8 || BASISD_SUPPORT_ETC2_EAC_RG11
+ static const dxt_selector_range s_etc2_eac_selector_ranges[] =
+ {
+ { 0, 3 },
+
+ { 1, 3 },
+ { 0, 2 },
+
+ { 1, 2 },
+ };
+
+ const uint32_t NUM_ETC2_EAC_SELECTOR_RANGES = sizeof(s_etc2_eac_selector_ranges) / sizeof(s_etc2_eac_selector_ranges[0]);
+
+ struct etc1_g_to_eac_conversion
+ {
+ uint8_t m_base;
+ uint8_t m_table_mul; // mul*16+table
+ uint16_t m_trans; // translates ETC1 selectors to ETC2_EAC_A8
+ };
+#endif // BASISD_SUPPORT_ETC2_EAC_A8 || BASISD_SUPPORT_ETC2_EAC_RG11
+
+#if BASISD_SUPPORT_ETC2_EAC_A8
+
+#if BASISD_WRITE_NEW_ETC2_EAC_A8_TABLES
+ struct pack_eac_a8_results
+ {
+ uint32_t m_base;
+ uint32_t m_table;
+ uint32_t m_multiplier;
+ basisu::vector m_selectors;
+ basisu::vector m_selectors_temp;
+ };
+
+ static uint64_t pack_eac_a8_exhaustive(pack_eac_a8_results& results, const uint8_t* pPixels, uint32_t num_pixels)
+ {
+ results.m_selectors.resize(num_pixels);
+ results.m_selectors_temp.resize(num_pixels);
+
+ uint64_t best_err = UINT64_MAX;
+
+ for (uint32_t base_color = 0; base_color < 256; base_color++)
+ {
+ for (uint32_t multiplier = 1; multiplier < 16; multiplier++)
+ {
+ for (uint32_t table = 0; table < 16; table++)
+ {
+ uint64_t total_err = 0;
+
+ for (uint32_t i = 0; i < num_pixels; i++)
+ {
+ const int a = pPixels[i];
+
+ uint32_t best_s_err = UINT32_MAX;
+ uint32_t best_s = 0;
+ for (uint32_t s = 0; s < 8; s++)
+ {
+ int v = (int)multiplier * g_eac_modifier_table[table][s] + (int)base_color;
+ if (v < 0)
+ v = 0;
+ else if (v > 255)
+ v = 255;
+
+ uint32_t err = abs(a - v);
+ if (err < best_s_err)
+ {
+ best_s_err = err;
+ best_s = s;
+ }
+ }
+
+ results.m_selectors_temp[i] = static_cast(best_s);
+
+ total_err += best_s_err * best_s_err;
+ if (total_err >= best_err)
+ break;
+ }
+
+ if (total_err < best_err)
+ {
+ best_err = total_err;
+ results.m_base = base_color;
+ results.m_multiplier = multiplier;
+ results.m_table = table;
+ results.m_selectors.swap(results.m_selectors_temp);
+ }
+
+ } // table
+
+ } // multiplier
+
+ } // base_color
+
+ return best_err;
+ }
+#endif // BASISD_WRITE_NEW_ETC2_EAC_A8_TABLES
+
+ static
+#if !BASISD_WRITE_NEW_ETC2_EAC_A8_TABLES
+ const
+#endif
+ etc1_g_to_eac_conversion s_etc1_g_to_etc2_a8[32 * 8][NUM_ETC2_EAC_SELECTOR_RANGES] =
+ {
+ { { 0,1,3328 },{ 0,1,3328 },{ 0,1,256 },{ 0,1,256 } },
+ { { 0,226,3936 },{ 0,226,3936 },{ 0,81,488 },{ 0,81,488 } },
+ { { 6,178,4012 },{ 6,178,4008 },{ 0,146,501 },{ 0,130,496 } },
+ { { 14,178,4012 },{ 14,178,4008 },{ 8,146,501 },{ 6,82,496 } },
+ { { 23,178,4012 },{ 23,178,4008 },{ 17,146,501 },{ 3,228,496 } },
+ { { 31,178,4012 },{ 31,178,4008 },{ 25,146,501 },{ 11,228,496 } },
+ { { 39,178,4012 },{ 39,178,4008 },{ 33,146,501 },{ 19,228,496 } },
+ { { 47,178,4012 },{ 47,178,4008 },{ 41,146,501 },{ 27,228,496 } },
+ { { 56,178,4012 },{ 56,178,4008 },{ 50,146,501 },{ 36,228,496 } },
+ { { 64,178,4012 },{ 64,178,4008 },{ 58,146,501 },{ 44,228,496 } },
+ { { 72,178,4012 },{ 72,178,4008 },{ 66,146,501 },{ 52,228,496 } },
+ { { 80,178,4012 },{ 80,178,4008 },{ 74,146,501 },{ 60,228,496 } },
+ { { 89,178,4012 },{ 89,178,4008 },{ 83,146,501 },{ 69,228,496 } },
+ { { 97,178,4012 },{ 97,178,4008 },{ 91,146,501 },{ 77,228,496 } },
+ { { 105,178,4012 },{ 105,178,4008 },{ 99,146,501 },{ 85,228,496 } },
+ { { 113,178,4012 },{ 113,178,4008 },{ 107,146,501 },{ 93,228,496 } },
+ { { 122,178,4012 },{ 122,178,4008 },{ 116,146,501 },{ 102,228,496 } },
+ { { 130,178,4012 },{ 130,178,4008 },{ 124,146,501 },{ 110,228,496 } },
+ { { 138,178,4012 },{ 138,178,4008 },{ 132,146,501 },{ 118,228,496 } },
+ { { 146,178,4012 },{ 146,178,4008 },{ 140,146,501 },{ 126,228,496 } },
+ { { 155,178,4012 },{ 155,178,4008 },{ 149,146,501 },{ 135,228,496 } },
+ { { 163,178,4012 },{ 163,178,4008 },{ 157,146,501 },{ 143,228,496 } },
+ { { 171,178,4012 },{ 171,178,4008 },{ 165,146,501 },{ 151,228,496 } },
+ { { 179,178,4012 },{ 179,178,4008 },{ 173,146,501 },{ 159,228,496 } },
+ { { 188,178,4012 },{ 188,178,4008 },{ 182,146,501 },{ 168,228,496 } },
+ { { 196,178,4012 },{ 196,178,4008 },{ 190,146,501 },{ 176,228,496 } },
+ { { 204,178,4012 },{ 204,178,4008 },{ 198,146,501 },{ 184,228,496 } },
+ { { 212,178,4012 },{ 212,178,4008 },{ 206,146,501 },{ 192,228,496 } },
+ { { 221,178,4012 },{ 221,178,4008 },{ 215,146,501 },{ 201,228,496 } },
+ { { 229,178,4012 },{ 229,178,4008 },{ 223,146,501 },{ 209,228,496 } },
+ { { 235,66,4012 },{ 221,100,4008 },{ 231,146,501 },{ 217,228,496 } },
+ { { 211,102,4085 },{ 118,31,4080 },{ 211,102,501 },{ 118,31,496 } },
+ { { 1,2,3328 },{ 1,2,3328 },{ 0,1,320 },{ 0,1,320 } },
+ { { 7,162,3905 },{ 7,162,3904 },{ 1,17,480 },{ 1,17,480 } },
+ { { 15,162,3906 },{ 15,162,3904 },{ 1,117,352 },{ 1,117,352 } },
+ { { 23,162,3906 },{ 23,162,3904 },{ 5,34,500 },{ 4,53,424 } },
+ { { 32,162,3906 },{ 32,162,3904 },{ 14,34,500 },{ 3,69,424 } },
+ { { 40,162,3906 },{ 40,162,3904 },{ 22,34,500 },{ 1,133,496 } },
+ { { 48,162,3906 },{ 48,162,3904 },{ 30,34,500 },{ 4,85,496 } },
+ { { 56,162,3906 },{ 56,162,3904 },{ 38,34,500 },{ 12,85,496 } },
+ { { 65,162,3906 },{ 65,162,3904 },{ 47,34,500 },{ 1,106,424 } },
+ { { 73,162,3906 },{ 73,162,3904 },{ 55,34,500 },{ 9,106,424 } },
+ { { 81,162,3906 },{ 81,162,3904 },{ 63,34,500 },{ 7,234,496 } },
+ { { 89,162,3906 },{ 89,162,3904 },{ 71,34,500 },{ 15,234,496 } },
+ { { 98,162,3906 },{ 98,162,3904 },{ 80,34,500 },{ 24,234,496 } },
+ { { 106,162,3906 },{ 106,162,3904 },{ 88,34,500 },{ 32,234,496 } },
+ { { 114,162,3906 },{ 114,162,3904 },{ 96,34,500 },{ 40,234,496 } },
+ { { 122,162,3906 },{ 122,162,3904 },{ 104,34,500 },{ 48,234,496 } },
+ { { 131,162,3906 },{ 131,162,3904 },{ 113,34,500 },{ 57,234,496 } },
+ { { 139,162,3906 },{ 139,162,3904 },{ 121,34,500 },{ 65,234,496 } },
+ { { 147,162,3906 },{ 147,162,3904 },{ 129,34,500 },{ 73,234,496 } },
+ { { 155,162,3906 },{ 155,162,3904 },{ 137,34,500 },{ 81,234,496 } },
+ { { 164,162,3906 },{ 164,162,3904 },{ 146,34,500 },{ 90,234,496 } },
+ { { 172,162,3906 },{ 172,162,3904 },{ 154,34,500 },{ 98,234,496 } },
+ { { 180,162,3906 },{ 180,162,3904 },{ 162,34,500 },{ 106,234,496 } },
+ { { 188,162,3906 },{ 188,162,3904 },{ 170,34,500 },{ 114,234,496 } },
+ { { 197,162,3906 },{ 197,162,3904 },{ 179,34,500 },{ 123,234,496 } },
+ { { 205,162,3906 },{ 205,162,3904 },{ 187,34,500 },{ 131,234,496 } },
+ { { 213,162,3906 },{ 213,162,3904 },{ 195,34,500 },{ 139,234,496 } },
+ { { 221,162,3906 },{ 221,162,3904 },{ 203,34,500 },{ 147,234,496 } },
+ { { 230,162,3906 },{ 230,162,3904 },{ 212,34,500 },{ 156,234,496 } },
+ { { 238,162,3906 },{ 174,106,4008 },{ 220,34,500 },{ 164,234,496 } },
+ { { 240,178,4001 },{ 182,106,4008 },{ 228,34,500 },{ 172,234,496 } },
+ { { 166,108,4085 },{ 115,31,4080 },{ 166,108,501 },{ 115,31,496 } },
+ { { 1,68,3328 },{ 1,68,3328 },{ 0,17,384 },{ 0,17,384 } },
+ { { 1,148,3904 },{ 1,148,3904 },{ 1,2,384 },{ 1,2,384 } },
+ { { 21,18,3851 },{ 21,18,3848 },{ 1,50,488 },{ 1,50,488 } },
+ { { 27,195,3851 },{ 29,18,3848 },{ 0,67,488 },{ 0,67,488 } },
+ { { 34,195,3907 },{ 38,18,3848 },{ 20,66,482 },{ 0,3,496 } },
+ { { 42,195,3907 },{ 46,18,3848 },{ 28,66,482 },{ 2,6,424 } },
+ { { 50,195,3907 },{ 54,18,3848 },{ 36,66,482 },{ 4,22,424 } },
+ { { 58,195,3907 },{ 62,18,3848 },{ 44,66,482 },{ 3,73,424 } },
+ { { 67,195,3907 },{ 71,18,3848 },{ 53,66,482 },{ 3,22,496 } },
+ { { 75,195,3907 },{ 79,18,3848 },{ 61,66,482 },{ 2,137,496 } },
+ { { 83,195,3907 },{ 87,18,3848 },{ 69,66,482 },{ 1,89,496 } },
+ { { 91,195,3907 },{ 95,18,3848 },{ 77,66,482 },{ 9,89,496 } },
+ { { 100,195,3907 },{ 104,18,3848 },{ 86,66,482 },{ 18,89,496 } },
+ { { 108,195,3907 },{ 112,18,3848 },{ 94,66,482 },{ 26,89,496 } },
+ { { 116,195,3907 },{ 120,18,3848 },{ 102,66,482 },{ 34,89,496 } },
+ { { 124,195,3907 },{ 128,18,3848 },{ 110,66,482 },{ 42,89,496 } },
+ { { 133,195,3907 },{ 137,18,3848 },{ 119,66,482 },{ 51,89,496 } },
+ { { 141,195,3907 },{ 145,18,3848 },{ 127,66,482 },{ 59,89,496 } },
+ { { 149,195,3907 },{ 153,18,3848 },{ 135,66,482 },{ 67,89,496 } },
+ { { 157,195,3907 },{ 161,18,3848 },{ 143,66,482 },{ 75,89,496 } },
+ { { 166,195,3907 },{ 170,18,3848 },{ 152,66,482 },{ 84,89,496 } },
+ { { 174,195,3907 },{ 178,18,3848 },{ 160,66,482 },{ 92,89,496 } },
+ { { 182,195,3907 },{ 186,18,3848 },{ 168,66,482 },{ 100,89,496 } },
+ { { 190,195,3907 },{ 194,18,3848 },{ 176,66,482 },{ 108,89,496 } },
+ { { 199,195,3907 },{ 203,18,3848 },{ 185,66,482 },{ 117,89,496 } },
+ { { 207,195,3907 },{ 211,18,3848 },{ 193,66,482 },{ 125,89,496 } },
+ { { 215,195,3907 },{ 219,18,3848 },{ 201,66,482 },{ 133,89,496 } },
+ { { 223,195,3907 },{ 227,18,3848 },{ 209,66,482 },{ 141,89,496 } },
+ { { 231,195,3907 },{ 168,89,4008 },{ 218,66,482 },{ 150,89,496 } },
+ { { 236,18,3907 },{ 176,89,4008 },{ 226,66,482 },{ 158,89,496 } },
+ { { 158,90,4085 },{ 103,31,4080 },{ 158,90,501 },{ 103,31,496 } },
+ { { 166,90,4085 },{ 111,31,4080 },{ 166,90,501 },{ 111,31,496 } },
+ { { 0,70,3328 },{ 0,70,3328 },{ 0,45,256 },{ 0,45,256 } },
+ { { 0,117,3904 },{ 0,117,3904 },{ 0,35,384 },{ 0,35,384 } },
+ { { 13,165,3905 },{ 13,165,3904 },{ 3,221,416 },{ 3,221,416 } },
+ { { 21,165,3906 },{ 21,165,3904 },{ 11,221,416 },{ 11,221,416 } },
+ { { 30,165,3906 },{ 30,165,3904 },{ 7,61,352 },{ 7,61,352 } },
+ { { 38,165,3906 },{ 38,165,3904 },{ 2,125,352 },{ 2,125,352 } },
+ { { 46,165,3906 },{ 46,165,3904 },{ 2,37,500 },{ 10,125,352 } },
+ { { 54,165,3906 },{ 54,165,3904 },{ 10,37,500 },{ 5,61,424 } },
+ { { 63,165,3906 },{ 63,165,3904 },{ 19,37,500 },{ 1,189,424 } },
+ { { 4,254,4012 },{ 71,165,3904 },{ 27,37,500 },{ 9,189,424 } },
+ { { 12,254,4012 },{ 79,165,3904 },{ 35,37,500 },{ 4,77,424 } },
+ { { 20,254,4012 },{ 87,165,3904 },{ 43,37,500 },{ 12,77,424 } },
+ { { 29,254,4012 },{ 96,165,3904 },{ 52,37,500 },{ 8,93,424 } },
+ { { 37,254,4012 },{ 104,165,3904 },{ 60,37,500 },{ 3,141,496 } },
+ { { 45,254,4012 },{ 112,165,3904 },{ 68,37,500 },{ 11,141,496 } },
+ { { 53,254,4012 },{ 120,165,3904 },{ 76,37,500 },{ 6,93,496 } },
+ { { 62,254,4012 },{ 129,165,3904 },{ 85,37,500 },{ 15,93,496 } },
+ { { 70,254,4012 },{ 137,165,3904 },{ 93,37,500 },{ 23,93,496 } },
+ { { 78,254,4012 },{ 145,165,3904 },{ 101,37,500 },{ 31,93,496 } },
+ { { 86,254,4012 },{ 153,165,3904 },{ 109,37,500 },{ 39,93,496 } },
+ { { 95,254,4012 },{ 162,165,3904 },{ 118,37,500 },{ 48,93,496 } },
+ { { 103,254,4012 },{ 170,165,3904 },{ 126,37,500 },{ 56,93,496 } },
+ { { 111,254,4012 },{ 178,165,3904 },{ 134,37,500 },{ 64,93,496 } },
+ { { 119,254,4012 },{ 186,165,3904 },{ 142,37,500 },{ 72,93,496 } },
+ { { 128,254,4012 },{ 195,165,3904 },{ 151,37,500 },{ 81,93,496 } },
+ { { 136,254,4012 },{ 203,165,3904 },{ 159,37,500 },{ 89,93,496 } },
+ { { 212,165,3906 },{ 136,77,4008 },{ 167,37,500 },{ 97,93,496 } },
+ { { 220,165,3394 },{ 131,93,4008 },{ 175,37,500 },{ 105,93,496 } },
+ { { 214,181,4001 },{ 140,93,4008 },{ 184,37,500 },{ 114,93,496 } },
+ { { 222,181,4001 },{ 148,93,4008 },{ 192,37,500 },{ 122,93,496 } },
+ { { 114,95,4085 },{ 99,31,4080 },{ 114,95,501 },{ 99,31,496 } },
+ { { 122,95,4085 },{ 107,31,4080 },{ 122,95,501 },{ 107,31,496 } },
+ { { 0,102,3840 },{ 0,102,3840 },{ 0,18,384 },{ 0,18,384 } },
+ { { 5,167,3904 },{ 5,167,3904 },{ 0,13,256 },{ 0,13,256 } },
+ { { 4,54,3968 },{ 4,54,3968 },{ 1,67,448 },{ 1,67,448 } },
+ { { 30,198,3850 },{ 30,198,3848 },{ 0,3,480 },{ 0,3,480 } },
+ { { 39,198,3850 },{ 39,198,3848 },{ 3,52,488 },{ 3,52,488 } },
+ { { 47,198,3851 },{ 47,198,3848 },{ 3,4,488 },{ 3,4,488 } },
+ { { 55,198,3851 },{ 55,198,3848 },{ 1,70,488 },{ 1,70,488 } },
+ { { 54,167,3906 },{ 63,198,3848 },{ 3,22,488 },{ 3,22,488 } },
+ { { 62,167,3906 },{ 72,198,3848 },{ 24,118,488 },{ 0,6,496 } },
+ { { 70,167,3906 },{ 80,198,3848 },{ 32,118,488 },{ 2,89,488 } },
+ { { 78,167,3906 },{ 88,198,3848 },{ 40,118,488 },{ 1,73,496 } },
+ { { 86,167,3906 },{ 96,198,3848 },{ 48,118,488 },{ 0,28,424 } },
+ { { 95,167,3906 },{ 105,198,3848 },{ 57,118,488 },{ 9,28,424 } },
+ { { 103,167,3906 },{ 113,198,3848 },{ 65,118,488 },{ 5,108,496 } },
+ { { 111,167,3906 },{ 121,198,3848 },{ 73,118,488 },{ 13,108,496 } },
+ { { 119,167,3906 },{ 129,198,3848 },{ 81,118,488 },{ 21,108,496 } },
+ { { 128,167,3906 },{ 138,198,3848 },{ 90,118,488 },{ 6,28,496 } },
+ { { 136,167,3906 },{ 146,198,3848 },{ 98,118,488 },{ 14,28,496 } },
+ { { 144,167,3906 },{ 154,198,3848 },{ 106,118,488 },{ 22,28,496 } },
+ { { 152,167,3906 },{ 162,198,3848 },{ 114,118,488 },{ 30,28,496 } },
+ { { 161,167,3906 },{ 171,198,3848 },{ 123,118,488 },{ 39,28,496 } },
+ { { 169,167,3906 },{ 179,198,3848 },{ 131,118,488 },{ 47,28,496 } },
+ { { 177,167,3906 },{ 187,198,3848 },{ 139,118,488 },{ 55,28,496 } },
+ { { 185,167,3906 },{ 195,198,3848 },{ 147,118,488 },{ 63,28,496 } },
+ { { 194,167,3906 },{ 120,12,4008 },{ 156,118,488 },{ 72,28,496 } },
+ { { 206,198,3907 },{ 116,28,4008 },{ 164,118,488 },{ 80,28,496 } },
+ { { 214,198,3907 },{ 124,28,4008 },{ 172,118,488 },{ 88,28,496 } },
+ { { 222,198,3395 },{ 132,28,4008 },{ 180,118,488 },{ 96,28,496 } },
+ { { 207,134,4001 },{ 141,28,4008 },{ 189,118,488 },{ 105,28,496 } },
+ { { 95,30,4085 },{ 86,31,4080 },{ 95,30,501 },{ 86,31,496 } },
+ { { 103,30,4085 },{ 94,31,4080 },{ 103,30,501 },{ 94,31,496 } },
+ { { 111,30,4085 },{ 102,31,4080 },{ 111,30,501 },{ 102,31,496 } },
+ { { 0,104,3840 },{ 0,104,3840 },{ 0,18,448 },{ 0,18,448 } },
+ { { 4,39,3904 },{ 4,39,3904 },{ 0,4,384 },{ 0,4,384 } },
+ { { 0,56,3968 },{ 0,56,3968 },{ 0,84,448 },{ 0,84,448 } },
+ { { 6,110,3328 },{ 6,110,3328 },{ 0,20,448 },{ 0,20,448 } },
+ { { 41,200,3850 },{ 41,200,3848 },{ 1,4,480 },{ 1,4,480 } },
+ { { 49,200,3850 },{ 49,200,3848 },{ 1,8,416 },{ 1,8,416 } },
+ { { 57,200,3851 },{ 57,200,3848 },{ 1,38,488 },{ 1,38,488 } },
+ { { 65,200,3851 },{ 65,200,3848 },{ 1,120,488 },{ 1,120,488 } },
+ { { 74,200,3851 },{ 74,200,3848 },{ 2,72,488 },{ 2,72,488 } },
+ { { 69,6,3907 },{ 82,200,3848 },{ 2,24,488 },{ 2,24,488 } },
+ { { 77,6,3907 },{ 90,200,3848 },{ 26,120,488 },{ 10,24,488 } },
+ { { 97,63,3330 },{ 98,200,3848 },{ 34,120,488 },{ 2,8,496 } },
+ { { 106,63,3330 },{ 107,200,3848 },{ 43,120,488 },{ 3,92,488 } },
+ { { 114,63,3330 },{ 115,200,3848 },{ 51,120,488 },{ 11,92,488 } },
+ { { 122,63,3330 },{ 123,200,3848 },{ 59,120,488 },{ 7,76,496 } },
+ { { 130,63,3330 },{ 131,200,3848 },{ 67,120,488 },{ 15,76,496 } },
+ { { 139,63,3330 },{ 140,200,3848 },{ 76,120,488 },{ 24,76,496 } },
+ { { 147,63,3330 },{ 148,200,3848 },{ 84,120,488 },{ 32,76,496 } },
+ { { 155,63,3330 },{ 156,200,3848 },{ 92,120,488 },{ 40,76,496 } },
+ { { 163,63,3330 },{ 164,200,3848 },{ 100,120,488 },{ 48,76,496 } },
+ { { 172,63,3330 },{ 173,200,3848 },{ 109,120,488 },{ 57,76,496 } },
+ { { 184,6,3851 },{ 181,200,3848 },{ 117,120,488 },{ 65,76,496 } },
+ { { 192,6,3851 },{ 133,28,3936 },{ 125,120,488 },{ 73,76,496 } },
+ { { 189,200,3907 },{ 141,28,3936 },{ 133,120,488 },{ 81,76,496 } },
+ { { 198,200,3907 },{ 138,108,4000 },{ 142,120,488 },{ 90,76,496 } },
+ { { 206,200,3907 },{ 146,108,4000 },{ 150,120,488 },{ 98,76,496 } },
+ { { 214,200,3395 },{ 154,108,4000 },{ 158,120,488 },{ 106,76,496 } },
+ { { 190,136,4001 },{ 162,108,4000 },{ 166,120,488 },{ 114,76,496 } },
+ { { 123,30,4076 },{ 87,15,4080 },{ 123,30,492 },{ 87,15,496 } },
+ { { 117,110,4084 },{ 80,31,4080 },{ 117,110,500 },{ 80,31,496 } },
+ { { 125,110,4084 },{ 88,31,4080 },{ 125,110,500 },{ 88,31,496 } },
+ { { 133,110,4084 },{ 96,31,4080 },{ 133,110,500 },{ 96,31,496 } },
+ { { 9,56,3904 },{ 9,56,3904 },{ 0,67,448 },{ 0,67,448 } },
+ { { 1,8,3904 },{ 1,8,3904 },{ 1,84,448 },{ 1,84,448 } },
+ { { 1,124,3904 },{ 1,124,3904 },{ 0,39,384 },{ 0,39,384 } },
+ { { 9,124,3904 },{ 9,124,3904 },{ 1,4,448 },{ 1,4,448 } },
+ { { 6,76,3904 },{ 6,76,3904 },{ 0,70,448 },{ 0,70,448 } },
+ { { 62,6,3859 },{ 62,6,3856 },{ 2,38,480 },{ 2,38,480 } },
+ { { 70,6,3859 },{ 70,6,3856 },{ 5,43,416 },{ 5,43,416 } },
+ { { 78,6,3859 },{ 78,6,3856 },{ 2,11,416 },{ 2,11,416 } },
+ { { 87,6,3859 },{ 87,6,3856 },{ 0,171,488 },{ 0,171,488 } },
+ { { 67,8,3906 },{ 95,6,3856 },{ 8,171,488 },{ 8,171,488 } },
+ { { 75,8,3907 },{ 103,6,3856 },{ 5,123,488 },{ 5,123,488 } },
+ { { 83,8,3907 },{ 111,6,3856 },{ 2,75,488 },{ 2,75,488 } },
+ { { 92,8,3907 },{ 120,6,3856 },{ 0,27,488 },{ 0,27,488 } },
+ { { 100,8,3907 },{ 128,6,3856 },{ 8,27,488 },{ 8,27,488 } },
+ { { 120,106,3843 },{ 136,6,3856 },{ 100,6,387 },{ 16,27,488 } },
+ { { 128,106,3843 },{ 144,6,3856 },{ 108,6,387 },{ 2,11,496 } },
+ { { 137,106,3843 },{ 153,6,3856 },{ 117,6,387 },{ 11,11,496 } },
+ { { 145,106,3843 },{ 161,6,3856 },{ 125,6,387 },{ 19,11,496 } },
+ { { 163,8,3851 },{ 137,43,3904 },{ 133,6,387 },{ 27,11,496 } },
+ { { 171,8,3851 },{ 101,11,4000 },{ 141,6,387 },{ 35,11,496 } },
+ { { 180,8,3851 },{ 110,11,4000 },{ 150,6,387 },{ 44,11,496 } },
+ { { 188,8,3851 },{ 118,11,4000 },{ 158,6,387 },{ 52,11,496 } },
+ { { 172,72,3907 },{ 126,11,4000 },{ 166,6,387 },{ 60,11,496 } },
+ { { 174,6,3971 },{ 134,11,4000 },{ 174,6,387 },{ 68,11,496 } },
+ { { 183,6,3971 },{ 143,11,4000 },{ 183,6,387 },{ 77,11,496 } },
+ { { 191,6,3971 },{ 151,11,4000 },{ 191,6,387 },{ 85,11,496 } },
+ { { 199,6,3971 },{ 159,11,4000 },{ 199,6,387 },{ 93,11,496 } },
+ { { 92,12,4084 },{ 69,15,4080 },{ 92,12,500 },{ 69,15,496 } },
+ { { 101,12,4084 },{ 78,15,4080 },{ 101,12,500 },{ 78,15,496 } },
+ { { 109,12,4084 },{ 86,15,4080 },{ 109,12,500 },{ 86,15,496 } },
+ { { 117,12,4084 },{ 79,31,4080 },{ 117,12,500 },{ 79,31,496 } },
+ { { 125,12,4084 },{ 87,31,4080 },{ 125,12,500 },{ 87,31,496 } },
+ { { 71,8,3602 },{ 71,8,3600 },{ 2,21,384 },{ 2,21,384 } },
+ { { 79,8,3611 },{ 79,8,3608 },{ 0,69,448 },{ 0,69,448 } },
+ { { 87,8,3611 },{ 87,8,3608 },{ 0,23,384 },{ 0,23,384 } },
+ { { 95,8,3611 },{ 95,8,3608 },{ 1,5,448 },{ 1,5,448 } },
+ { { 104,8,3611 },{ 104,8,3608 },{ 0,88,448 },{ 0,88,448 } },
+ { { 112,8,3611 },{ 112,8,3608 },{ 0,72,448 },{ 0,72,448 } },
+ { { 120,8,3611 },{ 121,8,3608 },{ 36,21,458 },{ 36,21,456 } },
+ { { 133,47,3091 },{ 129,8,3608 },{ 44,21,458 },{ 44,21,456 } },
+ { { 142,47,3091 },{ 138,8,3608 },{ 53,21,459 },{ 53,21,456 } },
+ { { 98,12,3850 },{ 98,12,3848 },{ 61,21,459 },{ 61,21,456 } },
+ { { 106,12,3850 },{ 106,12,3848 },{ 10,92,480 },{ 69,21,456 } },
+ { { 114,12,3851 },{ 114,12,3848 },{ 18,92,480 },{ 77,21,456 } },
+ { { 87,12,3906 },{ 87,12,3904 },{ 3,44,488 },{ 86,21,456 } },
+ { { 95,12,3906 },{ 95,12,3904 },{ 11,44,488 },{ 94,21,456 } },
+ { { 103,12,3906 },{ 103,12,3904 },{ 19,44,488 },{ 102,21,456 } },
+ { { 111,12,3907 },{ 111,12,3904 },{ 27,44,489 },{ 110,21,456 } },
+ { { 120,12,3907 },{ 120,12,3904 },{ 36,44,489 },{ 119,21,456 } },
+ { { 128,12,3907 },{ 128,12,3904 },{ 44,44,489 },{ 127,21,456 } },
+ { { 136,12,3907 },{ 136,12,3904 },{ 52,44,489 },{ 135,21,456 } },
+ { { 144,12,3907 },{ 144,12,3904 },{ 60,44,489 },{ 143,21,456 } },
+ { { 153,12,3907 },{ 153,12,3904 },{ 69,44,490 },{ 152,21,456 } },
+ { { 161,12,3395 },{ 149,188,3968 },{ 77,44,490 },{ 160,21,456 } },
+ { { 169,12,3395 },{ 198,21,3928 },{ 85,44,490 },{ 168,21,456 } },
+ { { 113,95,4001 },{ 201,69,3992 },{ 125,8,483 },{ 176,21,456 } },
+ { { 122,95,4001 },{ 200,21,3984 },{ 134,8,483 },{ 185,21,456 } },
+ { { 142,8,4067 },{ 208,21,3984 },{ 142,8,483 },{ 193,21,456 } },
+ { { 151,8,4067 },{ 47,15,4080 },{ 151,8,483 },{ 47,15,496 } },
+ { { 159,8,4067 },{ 55,15,4080 },{ 159,8,483 },{ 55,15,496 } },
+ { { 168,8,4067 },{ 64,15,4080 },{ 168,8,483 },{ 64,15,496 } },
+ { { 160,40,4075 },{ 72,15,4080 },{ 160,40,491 },{ 72,15,496 } },
+ { { 168,40,4075 },{ 80,15,4080 },{ 168,40,491 },{ 80,15,496 } },
+ { { 144,8,4082 },{ 88,15,4080 },{ 144,8,498 },{ 88,15,496 } }
+ };
+#endif // BASISD_SUPPORT_ETC2_EAC_A8
+
+#if BASISD_WRITE_NEW_ETC2_EAC_A8_TABLES
+ static void create_etc2_eac_a8_conversion_table()
+ {
+ FILE* pFile = fopen("basisu_decoder_tables_etc2_eac_a8.inc", "w");
+
+ for (uint32_t inten = 0; inten < 8; inten++)
+ {
+ for (uint32_t base = 0; base < 32; base++)
+ {
+ color32 block_colors[4];
+ decoder_etc_block::get_diff_subblock_colors(block_colors, decoder_etc_block::pack_color5(color32(base, base, base, 255), false), inten);
+
+ fprintf(pFile, "{");
+
+ for (uint32_t sel_range = 0; sel_range < NUM_ETC2_EAC_SELECTOR_RANGES; sel_range++)
+ {
+ const uint32_t low_selector = s_etc2_eac_selector_ranges[sel_range].m_low;
+ const uint32_t high_selector = s_etc2_eac_selector_ranges[sel_range].m_high;
+
+ // We have a ETC1 base color and intensity, and a used selector range from low_selector-high_selector.
+ // Now find the best ETC2 EAC A8 base/table/multiplier that fits these colors.
+
+ uint8_t pixels[4];
+ uint32_t num_pixels = 0;
+ for (uint32_t s = low_selector; s <= high_selector; s++)
+ pixels[num_pixels++] = block_colors[s].g;
+
+ pack_eac_a8_results pack_results;
+ pack_eac_a8_exhaustive(pack_results, pixels, num_pixels);
+
+ etc1_g_to_eac_conversion& c = s_etc1_g_to_etc2_a8[base + inten * 32][sel_range];
+
+ c.m_base = pack_results.m_base;
+ c.m_table_mul = pack_results.m_table * 16 + pack_results.m_multiplier;
+ c.m_trans = 0;
+
+ for (uint32_t s = 0; s < 4; s++)
+ {
+ if ((s < low_selector) || (s > high_selector))
+ continue;
+
+ uint32_t etc2_selector = pack_results.m_selectors[s - low_selector];
+
+ c.m_trans |= (etc2_selector << (s * 3));
+ }
+
+ fprintf(pFile, "{%u,%u,%u}", c.m_base, c.m_table_mul, c.m_trans);
+ if (sel_range < (NUM_ETC2_EAC_SELECTOR_RANGES - 1))
+ fprintf(pFile, ",");
+ }
+
+ fprintf(pFile, "},\n");
+ }
+ }
+
+ fclose(pFile);
+ }
+#endif
+
+#if BASISD_WRITE_NEW_ETC2_EAC_R11_TABLES
+ struct pack_eac_r11_results
+ {
+ uint32_t m_base;
+ uint32_t m_table;
+ uint32_t m_multiplier;
+ basisu::vector m_selectors;
+ basisu::vector m_selectors_temp;
+ };
+
+ static uint64_t pack_eac_r11_exhaustive(pack_eac_r11_results& results, const uint8_t* pPixels, uint32_t num_pixels)
+ {
+ results.m_selectors.resize(num_pixels);
+ results.m_selectors_temp.resize(num_pixels);
+
+ uint64_t best_err = UINT64_MAX;
+
+ for (uint32_t base_color = 0; base_color < 256; base_color++)
+ {
+ for (uint32_t multiplier = 0; multiplier < 16; multiplier++)
+ {
+ for (uint32_t table = 0; table < 16; table++)
+ {
+ uint64_t total_err = 0;
+
+ for (uint32_t i = 0; i < num_pixels; i++)
+ {
+ // Convert 8-bit input to 11-bits
+ const int a = (pPixels[i] * 2047 + 128) / 255;
+
+ uint32_t best_s_err = UINT32_MAX;
+ uint32_t best_s = 0;
+ for (uint32_t s = 0; s < 8; s++)
+ {
+ int v = (int)(multiplier ? (multiplier * 8) : 1) * g_eac_modifier_table[table][s] + (int)base_color * 8 + 4;
+ if (v < 0)
+ v = 0;
+ else if (v > 2047)
+ v = 2047;
+
+ uint32_t err = abs(a - v);
+ if (err < best_s_err)
+ {
+ best_s_err = err;
+ best_s = s;
+ }
+ }
+
+ results.m_selectors_temp[i] = static_cast(best_s);
+
+ total_err += best_s_err * best_s_err;
+ if (total_err >= best_err)
+ break;
+ }
+
+ if (total_err < best_err)
+ {
+ best_err = total_err;
+ results.m_base = base_color;
+ results.m_multiplier = multiplier;
+ results.m_table = table;
+ results.m_selectors.swap(results.m_selectors_temp);
+ }
+
+ } // table
+
+ } // multiplier
+
+ } // base_color
+
+ return best_err;
+ }
+
+ static void create_etc2_eac_r11_conversion_table()
+ {
+ FILE* pFile = nullptr;
+ fopen_s(&pFile, "basisu_decoder_tables_etc2_eac_r11.inc", "w");
+
+ for (uint32_t inten = 0; inten < 8; inten++)
+ {
+ for (uint32_t base = 0; base < 32; base++)
+ {
+ color32 block_colors[4];
+ decoder_etc_block::get_diff_subblock_colors(block_colors, decoder_etc_block::pack_color5(color32(base, base, base, 255), false), inten);
+
+ fprintf(pFile, "{");
+
+ for (uint32_t sel_range = 0; sel_range < NUM_ETC2_EAC_SELECTOR_RANGES; sel_range++)
+ {
+ const uint32_t low_selector = s_etc2_eac_selector_ranges[sel_range].m_low;
+ const uint32_t high_selector = s_etc2_eac_selector_ranges[sel_range].m_high;
+
+ // We have a ETC1 base color and intensity, and a used selector range from low_selector-high_selector.
+ // Now find the best ETC2 EAC R11 base/table/multiplier that fits these colors.
+
+ uint8_t pixels[4];
+ uint32_t num_pixels = 0;
+ for (uint32_t s = low_selector; s <= high_selector; s++)
+ pixels[num_pixels++] = block_colors[s].g;
+
+ pack_eac_r11_results pack_results;
+ pack_eac_r11_exhaustive(pack_results, pixels, num_pixels);
+
+ etc1_g_to_eac_conversion c;
+
+ c.m_base = (uint8_t)pack_results.m_base;
+ c.m_table_mul = (uint8_t)(pack_results.m_table * 16 + pack_results.m_multiplier);
+ c.m_trans = 0;
+
+ for (uint32_t s = 0; s < 4; s++)
+ {
+ if ((s < low_selector) || (s > high_selector))
+ continue;
+
+ uint32_t etc2_selector = pack_results.m_selectors[s - low_selector];
+
+ c.m_trans |= (etc2_selector << (s * 3));
+ }
+
+ fprintf(pFile, "{%u,%u,%u}", c.m_base, c.m_table_mul, c.m_trans);
+ if (sel_range < (NUM_ETC2_EAC_SELECTOR_RANGES - 1))
+ fprintf(pFile, ",");
+ }
+
+ fprintf(pFile, "},\n");
+ }
+ }
+
+ fclose(pFile);
+ }
+#endif // BASISD_WRITE_NEW_ETC2_EAC_R11_TABLES
+
+#if BASISD_WRITE_NEW_ASTC_TABLES
+ static void create_etc1_to_astc_conversion_table_0_47();
+ static void create_etc1_to_astc_conversion_table_0_255();
+#endif
+
+#if BASISD_SUPPORT_ASTC
+ static void transcoder_init_astc();
+#endif
+
+#if BASISD_WRITE_NEW_BC7_MODE5_TABLES
+ static void create_etc1_to_bc7_m5_color_conversion_table();
+ static void create_etc1_to_bc7_m5_alpha_conversion_table();
+#endif
+
+#if BASISD_SUPPORT_BC7_MODE5
+ static void transcoder_init_bc7_mode5();
+#endif
+
+#if BASISD_WRITE_NEW_ATC_TABLES
+ static void create_etc1s_to_atc_conversion_tables();
+#endif
+
+#if BASISD_SUPPORT_ATC
+ static void transcoder_init_atc();
+#endif
+
+#if BASISD_SUPPORT_PVRTC2
+ static void transcoder_init_pvrtc2();
+#endif
+
+#if BASISD_SUPPORT_UASTC
+ void uastc_init();
+#endif
+
+ static bool g_transcoder_initialized;
+
+ // Library global initialization. Requires ~9 milliseconds when compiled and executed natively on a Core i7 2.2 GHz.
+ // If this is too slow, these computed tables can easilky be moved to be compiled in.
+ void basisu_transcoder_init()
+ {
+ if (g_transcoder_initialized)
+ {
+ BASISU_DEVEL_ERROR("basisu_transcoder::basisu_transcoder_init: Called more than once\n");
+ return;
+ }
+
+ BASISU_DEVEL_ERROR("basisu_transcoder::basisu_transcoder_init: Initializing (this is not an error)\n");
+
+#if BASISD_SUPPORT_UASTC
+ uastc_init();
+#endif
+
+#if BASISD_SUPPORT_ASTC
+ transcoder_init_astc();
+#endif
+
+#if BASISD_WRITE_NEW_ASTC_TABLES
+ create_etc1_to_astc_conversion_table_0_47();
+ create_etc1_to_astc_conversion_table_0_255();
+ exit(0);
+#endif
+
+#if BASISD_WRITE_NEW_BC7_MODE5_TABLES
+ create_etc1_to_bc7_m5_color_conversion_table();
+ create_etc1_to_bc7_m5_alpha_conversion_table();
+ exit(0);
+#endif
+
+#if BASISD_WRITE_NEW_DXT1_TABLES
+ create_etc1_to_dxt1_5_conversion_table();
+ create_etc1_to_dxt1_6_conversion_table();
+ exit(0);
+#endif
+
+#if BASISD_WRITE_NEW_ETC2_EAC_A8_TABLES
+ create_etc2_eac_a8_conversion_table();
+ exit(0);
+#endif
+
+#if BASISD_WRITE_NEW_ATC_TABLES
+ create_etc1s_to_atc_conversion_tables();
+ exit(0);
+#endif
+
+#if BASISD_WRITE_NEW_ETC2_EAC_R11_TABLES
+ create_etc2_eac_r11_conversion_table();
+ exit(0);
+#endif
+
+#if BASISD_SUPPORT_DXT1 || BASISD_SUPPORT_UASTC
+ uint8_t bc1_expand5[32];
+ for (int i = 0; i < 32; i++)
+ bc1_expand5[i] = static_cast((i << 3) | (i >> 2));
+ prepare_bc1_single_color_table(g_bc1_match5_equals_1, bc1_expand5, 32, 32, 1);
+ prepare_bc1_single_color_table(g_bc1_match5_equals_0, bc1_expand5, 1, 32, 0);
+
+ uint8_t bc1_expand6[64];
+ for (int i = 0; i < 64; i++)
+ bc1_expand6[i] = static_cast((i << 2) | (i >> 4));
+ prepare_bc1_single_color_table(g_bc1_match6_equals_1, bc1_expand6, 64, 64, 1);
+ prepare_bc1_single_color_table(g_bc1_match6_equals_0, bc1_expand6, 1, 64, 0);
+
+#if 0
+ for (uint32_t i = 0; i < 256; i++)
+ {
+ printf("%u %u %u\n", i, (i * 63 + 127) / 255, g_bc1_match6_equals_0[i].m_hi);
+ }
+ exit(0);
+#endif
+
+#endif
+
+#if BASISD_SUPPORT_DXT1
+ for (uint32_t i = 0; i < NUM_ETC1_TO_DXT1_SELECTOR_RANGES; i++)
+ {
+ uint32_t l = g_etc1_to_dxt1_selector_ranges[i].m_low;
+ uint32_t h = g_etc1_to_dxt1_selector_ranges[i].m_high;
+ g_etc1_to_dxt1_selector_range_index[l][h] = i;
+ }
+
+ for (uint32_t sm = 0; sm < NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS; sm++)
+ {
+ uint8_t etc1_to_dxt1_selector_mappings_raw_dxt1[4];
+ uint8_t etc1_to_dxt1_selector_mappings_raw_dxt1_inv[4];
+
+ for (uint32_t j = 0; j < 4; j++)
+ {
+ static const uint8_t s_linear_dxt1_to_dxt1[4] = { 0, 2, 3, 1 };
+ static const uint8_t s_dxt1_inverted_xlat[4] = { 1, 0, 3, 2 };
+
+ etc1_to_dxt1_selector_mappings_raw_dxt1[j] = (uint8_t)s_linear_dxt1_to_dxt1[g_etc1_to_dxt1_selector_mappings[sm][j]];
+ etc1_to_dxt1_selector_mappings_raw_dxt1_inv[j] = (uint8_t)s_dxt1_inverted_xlat[etc1_to_dxt1_selector_mappings_raw_dxt1[j]];
+ }
+
+ for (uint32_t i = 0; i < 256; i++)
+ {
+ uint32_t k = 0, k_inv = 0;
+ for (uint32_t s = 0; s < 4; s++)
+ {
+ k |= (etc1_to_dxt1_selector_mappings_raw_dxt1[(i >> (s * 2)) & 3] << (s * 2));
+ k_inv |= (etc1_to_dxt1_selector_mappings_raw_dxt1_inv[(i >> (s * 2)) & 3] << (s * 2));
+ }
+ g_etc1_to_dxt1_selector_mappings_raw_dxt1_256[sm][i] = (uint8_t)k;
+ g_etc1_to_dxt1_selector_mappings_raw_dxt1_inv_256[sm][i] = (uint8_t)k_inv;
+ }
+ }
+#endif
+
+#if BASISD_SUPPORT_BC7_MODE5
+ transcoder_init_bc7_mode5();
+#endif
+
+#if BASISD_SUPPORT_ATC
+ transcoder_init_atc();
+#endif
+
+#if BASISD_SUPPORT_PVRTC2
+ transcoder_init_pvrtc2();
+#endif
+
+ g_transcoder_initialized = true;
+ }
+
+#if BASISD_SUPPORT_DXT1
+ static void convert_etc1s_to_dxt1(dxt1_block* pDst_block, const endpoint *pEndpoints, const selector* pSelector, bool use_threecolor_blocks)
+ {
+#if !BASISD_WRITE_NEW_DXT1_TABLES
+ const uint32_t low_selector = pSelector->m_lo_selector;
+ const uint32_t high_selector = pSelector->m_hi_selector;
+
+ const color32& base_color = pEndpoints->m_color5;
+ const uint32_t inten_table = pEndpoints->m_inten5;
+
+ if (low_selector == high_selector)
+ {
+ uint32_t r, g, b;
+ decoder_etc_block::get_block_color5(base_color, inten_table, low_selector, r, g, b);
+
+ uint32_t mask = 0xAA;
+ uint32_t max16 = (g_bc1_match5_equals_1[r].m_hi << 11) | (g_bc1_match6_equals_1[g].m_hi << 5) | g_bc1_match5_equals_1[b].m_hi;
+ uint32_t min16 = (g_bc1_match5_equals_1[r].m_lo << 11) | (g_bc1_match6_equals_1[g].m_lo << 5) | g_bc1_match5_equals_1[b].m_lo;
+
+ if ((!use_threecolor_blocks) && (min16 == max16))
+ {
+ // This is an annoying edge case that impacts BC3.
+ // This is to guarantee that BC3 blocks never use punchthrough alpha (3 color) mode, which isn't supported on some (all?) GPU's.
+ mask = 0;
+
+ // Make l > h
+ if (min16 > 0)
+ min16--;
+ else
+ {
+ // l = h = 0
+ assert(min16 == max16 && max16 == 0);
+
+ max16 = 1;
+ min16 = 0;
+ mask = 0x55;
+ }
+
+ assert(max16 > min16);
+ }
+
+ if (max16 < min16)
+ {
+ std::swap(max16, min16);
+ mask ^= 0x55;
+ }
+
+ pDst_block->set_low_color(static_cast(max16));
+ pDst_block->set_high_color(static_cast(min16));
+ pDst_block->m_selectors[0] = static_cast(mask);
+ pDst_block->m_selectors[1] = static_cast(mask);
+ pDst_block->m_selectors[2] = static_cast(mask);
+ pDst_block->m_selectors[3] = static_cast(mask);
+
+ return;
+ }
+ else if ((inten_table >= 7) && (pSelector->m_num_unique_selectors == 2) && (pSelector->m_lo_selector == 0) && (pSelector->m_hi_selector == 3))
+ {
+ color32 block_colors[4];
+
+ decoder_etc_block::get_block_colors5(block_colors, base_color, inten_table);
+
+ const uint32_t r0 = block_colors[0].r;
+ const uint32_t g0 = block_colors[0].g;
+ const uint32_t b0 = block_colors[0].b;
+
+ const uint32_t r1 = block_colors[3].r;
+ const uint32_t g1 = block_colors[3].g;
+ const uint32_t b1 = block_colors[3].b;
+
+ uint32_t max16 = (g_bc1_match5_equals_0[r0].m_hi << 11) | (g_bc1_match6_equals_0[g0].m_hi << 5) | g_bc1_match5_equals_0[b0].m_hi;
+ uint32_t min16 = (g_bc1_match5_equals_0[r1].m_hi << 11) | (g_bc1_match6_equals_0[g1].m_hi << 5) | g_bc1_match5_equals_0[b1].m_hi;
+
+ uint32_t l = 0, h = 1;
+
+ if (min16 == max16)
+ {
+ // Make l > h
+ if (min16 > 0)
+ {
+ min16--;
+
+ l = 0;
+ h = 0;
+ }
+ else
+ {
+ // l = h = 0
+ assert(min16 == max16 && max16 == 0);
+
+ max16 = 1;
+ min16 = 0;
+
+ l = 1;
+ h = 1;
+ }
+
+ assert(max16 > min16);
+ }
+
+ if (max16 < min16)
+ {
+ std::swap(max16, min16);
+ l = 1;
+ h = 0;
+ }
+
+ pDst_block->set_low_color((uint16_t)max16);
+ pDst_block->set_high_color((uint16_t)min16);
+
+ for (uint32_t y = 0; y < 4; y++)
+ {
+ for (uint32_t x = 0; x < 4; x++)
+ {
+ uint32_t s = pSelector->get_selector(x, y);
+ pDst_block->set_selector(x, y, (s == 3) ? h : l);
+ }
+ }
+
+ return;
+ }
+
+ const uint32_t selector_range_table = g_etc1_to_dxt1_selector_range_index[low_selector][high_selector];
+
+ //[32][8][RANGES][MAPPING]
+ const etc1_to_dxt1_56_solution* pTable_r = &g_etc1_to_dxt_5[(inten_table * 32 + base_color.r) * (NUM_ETC1_TO_DXT1_SELECTOR_RANGES * NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS) + selector_range_table * NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS];
+ const etc1_to_dxt1_56_solution* pTable_g = &g_etc1_to_dxt_6[(inten_table * 32 + base_color.g) * (NUM_ETC1_TO_DXT1_SELECTOR_RANGES * NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS) + selector_range_table * NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS];
+ const etc1_to_dxt1_56_solution* pTable_b = &g_etc1_to_dxt_5[(inten_table * 32 + base_color.b) * (NUM_ETC1_TO_DXT1_SELECTOR_RANGES * NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS) + selector_range_table * NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS];
+
+ uint32_t best_err = UINT_MAX;
+ uint32_t best_mapping = 0;
+
+ assert(NUM_ETC1_TO_DXT1_SELECTOR_MAPPINGS == 10);
+#define DO_ITER(m) { uint32_t total_err = pTable_r[m].m_err + pTable_g[m].m_err + pTable_b[m].m_err; if (total_err < best_err) { best_err = total_err; best_mapping = m; } }
+ DO_ITER(0); DO_ITER(1); DO_ITER(2); DO_ITER(3); DO_ITER(4);
+ DO_ITER(5); DO_ITER(6); DO_ITER(7); DO_ITER(8); DO_ITER(9);
+#undef DO_ITER
+
+ uint32_t l = dxt1_block::pack_unscaled_color(pTable_r[best_mapping].m_lo, pTable_g[best_mapping].m_lo, pTable_b[best_mapping].m_lo);
+ uint32_t h = dxt1_block::pack_unscaled_color(pTable_r[best_mapping].m_hi, pTable_g[best_mapping].m_hi, pTable_b[best_mapping].m_hi);
+
+ const uint8_t* pSelectors_xlat_256 = &g_etc1_to_dxt1_selector_mappings_raw_dxt1_256[best_mapping][0];
+
+ if (l < h)
+ {
+ std::swap(l, h);
+ pSelectors_xlat_256 = &g_etc1_to_dxt1_selector_mappings_raw_dxt1_inv_256[best_mapping][0];
+ }
+
+ pDst_block->set_low_color(static_cast(l));
+ pDst_block->set_high_color(static_cast(h));
+
+ if (l == h)
+ {
+ uint8_t mask = 0;
+
+ if (!use_threecolor_blocks)
+ {
+ // This is an annoying edge case that impacts BC3.
+
+ // Make l > h
+ if (h > 0)
+ h--;
+ else
+ {
+ // l = h = 0
+ assert(l == h && h == 0);
+
+ h = 0;
+ l = 1;
+ mask = 0x55;
+ }
+
+ assert(l > h);
+ pDst_block->set_low_color(static_cast(l));
+ pDst_block->set_high_color(static_cast(h));
+ }
+
+ pDst_block->m_selectors[0] = mask;
+ pDst_block->m_selectors[1] = mask;
+ pDst_block->m_selectors[2] = mask;
+ pDst_block->m_selectors[3] = mask;
+
+ return;
+ }
+
+ pDst_block->m_selectors[0] = pSelectors_xlat_256[pSelector->m_selectors[0]];
+ pDst_block->m_selectors[1] = pSelectors_xlat_256[pSelector->m_selectors[1]];
+ pDst_block->m_selectors[2] = pSelectors_xlat_256[pSelector->m_selectors[2]];
+ pDst_block->m_selectors[3] = pSelectors_xlat_256[pSelector->m_selectors[3]];
+#endif
+ }
+
+#if BASISD_ENABLE_DEBUG_FLAGS
+ static void convert_etc1s_to_dxt1_vis(dxt1_block* pDst_block, const endpoint* pEndpoints, const selector* pSelector, bool use_threecolor_blocks)
+ {
+ convert_etc1s_to_dxt1(pDst_block, pEndpoints, pSelector, use_threecolor_blocks);
+
+ if (g_debug_flags & cDebugFlagVisBC1Sels)
+ {
+ uint32_t l = dxt1_block::pack_unscaled_color(31, 63, 31);
+ uint32_t h = dxt1_block::pack_unscaled_color(0, 0, 0);
+ pDst_block->set_low_color(static_cast(l));
+ pDst_block->set_high_color(static_cast(h));
+ }
+ else if (g_debug_flags & cDebugFlagVisBC1Endpoints)
+ {
+ for (uint32_t y = 0; y < 4; y++)
+ for (uint32_t x = 0; x < 4; x++)
+ pDst_block->set_selector(x, y, (y < 2) ? 0 : 1);
+ }
+ }
+#endif
+#endif
+
+#if BASISD_SUPPORT_FXT1
+ struct fxt1_block
+ {
+ union
+ {
+ struct
+ {
+ uint64_t m_t00 : 2;
+ uint64_t m_t01 : 2;
+ uint64_t m_t02 : 2;
+ uint64_t m_t03 : 2;
+ uint64_t m_t04 : 2;
+ uint64_t m_t05 : 2;
+ uint64_t m_t06 : 2;
+ uint64_t m_t07 : 2;
+ uint64_t m_t08 : 2;
+ uint64_t m_t09 : 2;
+ uint64_t m_t10 : 2;
+ uint64_t m_t11 : 2;
+ uint64_t m_t12 : 2;
+ uint64_t m_t13 : 2;
+ uint64_t m_t14 : 2;
+ uint64_t m_t15 : 2;
+ uint64_t m_t16 : 2;
+ uint64_t m_t17 : 2;
+ uint64_t m_t18 : 2;
+ uint64_t m_t19 : 2;
+ uint64_t m_t20 : 2;
+ uint64_t m_t21 : 2;
+ uint64_t m_t22 : 2;
+ uint64_t m_t23 : 2;
+ uint64_t m_t24 : 2;
+ uint64_t m_t25 : 2;
+ uint64_t m_t26 : 2;
+ uint64_t m_t27 : 2;
+ uint64_t m_t28 : 2;
+ uint64_t m_t29 : 2;
+ uint64_t m_t30 : 2;
+ uint64_t m_t31 : 2;
+ } m_lo;
+ uint64_t m_lo_bits;
+ uint8_t m_sels[8];
+ };
+ union
+ {
+ struct
+ {
+#ifdef BASISU_USE_ORIGINAL_3DFX_FXT1_ENCODING
+ uint64_t m_b1 : 5;
+ uint64_t m_g1 : 5;
+ uint64_t m_r1 : 5;
+ uint64_t m_b0 : 5;
+ uint64_t m_g0 : 5;
+ uint64_t m_r0 : 5;
+ uint64_t m_b3 : 5;
+ uint64_t m_g3 : 5;
+ uint64_t m_r3 : 5;
+ uint64_t m_b2 : 5;
+ uint64_t m_g2 : 5;
+ uint64_t m_r2 : 5;
+#else
+ uint64_t m_b0 : 5;
+ uint64_t m_g0 : 5;
+ uint64_t m_r0 : 5;
+ uint64_t m_b1 : 5;
+ uint64_t m_g1 : 5;
+ uint64_t m_r1 : 5;
+ uint64_t m_b2 : 5;
+ uint64_t m_g2 : 5;
+ uint64_t m_r2 : 5;
+ uint64_t m_b3 : 5;
+ uint64_t m_g3 : 5;
+ uint64_t m_r3 : 5;
+#endif
+ uint64_t m_alpha : 1;
+ uint64_t m_glsb : 2;
+ uint64_t m_mode : 1;
+ } m_hi;
+ uint64_t m_hi_bits;
+ };
+ };
+
+ static uint8_t conv_dxt1_to_fxt1_sels(uint32_t sels)
+ {
+ static uint8_t s_conv_table[16] = { 0, 3, 1, 2, 12, 15, 13, 14, 4, 7, 5, 6, 8, 11, 9, 10 };
+ return s_conv_table[sels & 15] | (s_conv_table[sels >> 4] << 4);
+ }
+
+ static void convert_etc1s_to_fxt1(void *pDst, const endpoint *pEndpoints, const selector *pSelectors, uint32_t fxt1_subblock)
+ {
+ fxt1_block* pBlock = static_cast(pDst);
+
+ // CC_MIXED is basically DXT1 with different encoding tricks.
+ // So transcode ETC1S to DXT1, then transcode that to FXT1 which is easy and nearly lossless.
+ // (It's not completely lossless because FXT1 rounds in its color lerps while DXT1 doesn't, but it should be good enough.)
+ dxt1_block blk;
+ convert_etc1s_to_dxt1(&blk, pEndpoints, pSelectors, false);
+
+ const uint32_t l = blk.get_low_color();
+ const uint32_t h = blk.get_high_color();
+
+ color32 color0((l >> 11) & 31, (l >> 5) & 63, l & 31, 255);
+ color32 color1((h >> 11) & 31, (h >> 5) & 63, h & 31, 255);
+
+ uint32_t g0 = color0.g & 1;
+ uint32_t g1 = color1.g & 1;
+
+ color0.g >>= 1;
+ color1.g >>= 1;
+
+ blk.m_selectors[0] = conv_dxt1_to_fxt1_sels(blk.m_selectors[0]);
+ blk.m_selectors[1] = conv_dxt1_to_fxt1_sels(blk.m_selectors[1]);
+ blk.m_selectors[2] = conv_dxt1_to_fxt1_sels(blk.m_selectors[2]);
+ blk.m_selectors[3] = conv_dxt1_to_fxt1_sels(blk.m_selectors[3]);
+
+ if ((blk.get_selector(0, 0) >> 1) != (g0 ^ g1))
+ {
+ std::swap(color0, color1);
+ std::swap(g0, g1);
+
+ blk.m_selectors[0] ^= 0xFF;
+ blk.m_selectors[1] ^= 0xFF;
+ blk.m_selectors[2] ^= 0xFF;
+ blk.m_selectors[3] ^= 0xFF;
+ }
+
+ if (fxt1_subblock == 0)
+ {
+ pBlock->m_hi.m_mode = 1;
+ pBlock->m_hi.m_alpha = 0;
+ pBlock->m_hi.m_glsb = g1 | (g1 << 1);
+ pBlock->m_hi.m_r0 = color0.r;
+ pBlock->m_hi.m_g0 = color0.g;
+ pBlock->m_hi.m_b0 = color0.b;
+ pBlock->m_hi.m_r1 = color1.r;
+ pBlock->m_hi.m_g1 = color1.g;
+ pBlock->m_hi.m_b1 = color1.b;
+ pBlock->m_hi.m_r2 = color0.r;
+ pBlock->m_hi.m_g2 = color0.g;
+ pBlock->m_hi.m_b2 = color0.b;
+ pBlock->m_hi.m_r3 = color1.r;
+ pBlock->m_hi.m_g3 = color1.g;
+ pBlock->m_hi.m_b3 = color1.b;
+ pBlock->m_sels[0] = blk.m_selectors[0];
+ pBlock->m_sels[1] = blk.m_selectors[1];
+ pBlock->m_sels[2] = blk.m_selectors[2];
+ pBlock->m_sels[3] = blk.m_selectors[3];
+
+ static const uint8_t s_border_dup[4] = { 0, 85, 170, 255 };
+ pBlock->m_sels[4] = s_border_dup[blk.m_selectors[0] >> 6];
+ pBlock->m_sels[5] = s_border_dup[blk.m_selectors[1] >> 6];
+ pBlock->m_sels[6] = s_border_dup[blk.m_selectors[2] >> 6];
+ pBlock->m_sels[7] = s_border_dup[blk.m_selectors[3] >> 6];
+ }
+ else
+ {
+ pBlock->m_hi.m_glsb = (pBlock->m_hi.m_glsb & 1) | (g1 << 1);
+ pBlock->m_hi.m_r2 = color0.r;
+ pBlock->m_hi.m_g2 = color0.g;
+ pBlock->m_hi.m_b2 = color0.b;
+ pBlock->m_hi.m_r3 = color1.r;
+ pBlock->m_hi.m_g3 = color1.g;
+ pBlock->m_hi.m_b3 = color1.b;
+ pBlock->m_sels[4] = blk.m_selectors[0];
+ pBlock->m_sels[5] = blk.m_selectors[1];
+ pBlock->m_sels[6] = blk.m_selectors[2];
+ pBlock->m_sels[7] = blk.m_selectors[3];
+ }
+ }
+#endif // BASISD_SUPPORT_FXT1
+#if BASISD_SUPPORT_DXT5A
+ static dxt_selector_range s_dxt5a_selector_ranges[] =
+ {
+ { 0, 3 },
+
+ { 1, 3 },
+ { 0, 2 },
+
+ { 1, 2 },
+ };
+
+ const uint32_t NUM_DXT5A_SELECTOR_RANGES = sizeof(s_dxt5a_selector_ranges) / sizeof(s_dxt5a_selector_ranges[0]);
+
+ struct etc1_g_to_dxt5a_conversion
+ {
+ uint8_t m_lo, m_hi;
+ uint16_t m_trans;
+ };
+
+ static etc1_g_to_dxt5a_conversion g_etc1_g_to_dxt5a[32 * 8][NUM_DXT5A_SELECTOR_RANGES] =
+ {
+ { { 8, 0, 393 },{ 8, 0, 392 },{ 2, 0, 9 },{ 2, 0, 8 }, }, { { 6, 16, 710 },{ 16, 6, 328 },{ 0, 10, 96 },{ 10, 6, 8 }, },
+ { { 28, 5, 1327 },{ 24, 14, 328 },{ 8, 18, 96 },{ 18, 14, 8 }, }, { { 36, 13, 1327 },{ 32, 22, 328 },{ 16, 26, 96 },{ 26, 22, 8 }, },
+ { { 45, 22, 1327 },{ 41, 31, 328 },{ 25, 35, 96 },{ 35, 31, 8 }, }, { { 53, 30, 1327 },{ 49, 39, 328 },{ 33, 43, 96 },{ 43, 39, 8 }, },
+ { { 61, 38, 1327 },{ 57, 47, 328 },{ 41, 51, 96 },{ 51, 47, 8 }, }, { { 69, 46, 1327 },{ 65, 55, 328 },{ 49, 59, 96 },{ 59, 55, 8 }, },
+ { { 78, 55, 1327 },{ 74, 64, 328 },{ 58, 68, 96 },{ 68, 64, 8 }, }, { { 86, 63, 1327 },{ 82, 72, 328 },{ 66, 76, 96 },{ 76, 72, 8 }, },
+ { { 94, 71, 1327 },{ 90, 80, 328 },{ 74, 84, 96 },{ 84, 80, 8 }, }, { { 102, 79, 1327 },{ 98, 88, 328 },{ 82, 92, 96 },{ 92, 88, 8 }, },
+ { { 111, 88, 1327 },{ 107, 97, 328 },{ 91, 101, 96 },{ 101, 97, 8 }, }, { { 119, 96, 1327 },{ 115, 105, 328 },{ 99, 109, 96 },{ 109, 105, 8 }, },
+ { { 127, 104, 1327 },{ 123, 113, 328 },{ 107, 117, 96 },{ 117, 113, 8 }, }, { { 135, 112, 1327 },{ 131, 121, 328 },{ 115, 125, 96 },{ 125, 121, 8 }, },
+ { { 144, 121, 1327 },{ 140, 130, 328 },{ 124, 134, 96 },{ 134, 130, 8 }, }, { { 152, 129, 1327 },{ 148, 138, 328 },{ 132, 142, 96 },{ 142, 138, 8 }, },
+ { { 160, 137, 1327 },{ 156, 146, 328 },{ 140, 150, 96 },{ 150, 146, 8 }, }, { { 168, 145, 1327 },{ 164, 154, 328 },{ 148, 158, 96 },{ 158, 154, 8 }, },
+ { { 177, 154, 1327 },{ 173, 163, 328 },{ 157, 167, 96 },{ 167, 163, 8 }, }, { { 185, 162, 1327 },{ 181, 171, 328 },{ 165, 175, 96 },{ 175, 171, 8 }, },
+ { { 193, 170, 1327 },{ 189, 179, 328 },{ 173, 183, 96 },{ 183, 179, 8 }, }, { { 201, 178, 1327 },{ 197, 187, 328 },{ 181, 191, 96 },{ 191, 187, 8 }, },
+ { { 210, 187, 1327 },{ 206, 196, 328 },{ 190, 200, 96 },{ 200, 196, 8 }, }, { { 218, 195, 1327 },{ 214, 204, 328 },{ 198, 208, 96 },{ 208, 204, 8 }, },
+ { { 226, 203, 1327 },{ 222, 212, 328 },{ 206, 216, 96 },{ 216, 212, 8 }, }, { { 234, 211, 1327 },{ 230, 220, 328 },{ 214, 224, 96 },{ 224, 220, 8 }, },
+ { { 243, 220, 1327 },{ 239, 229, 328 },{ 223, 233, 96 },{ 233, 229, 8 }, }, { { 251, 228, 1327 },{ 247, 237, 328 },{ 231, 241, 96 },{ 241, 237, 8 }, },
+ { { 239, 249, 3680 },{ 245, 249, 3648 },{ 239, 249, 96 },{ 249, 245, 8 }, }, { { 247, 253, 4040 },{ 255, 253, 8 },{ 247, 253, 456 },{ 255, 253, 8 }, },
+ { { 5, 17, 566 },{ 5, 17, 560 },{ 5, 0, 9 },{ 5, 0, 8 }, }, { { 25, 0, 313 },{ 25, 3, 328 },{ 13, 0, 49 },{ 13, 3, 8 }, },
+ { { 39, 0, 1329 },{ 33, 11, 328 },{ 11, 21, 70 },{ 21, 11, 8 }, }, { { 47, 7, 1329 },{ 41, 19, 328 },{ 29, 7, 33 },{ 29, 19, 8 }, },
+ { { 50, 11, 239 },{ 50, 28, 328 },{ 38, 16, 33 },{ 38, 28, 8 }, }, { { 92, 13, 2423 },{ 58, 36, 328 },{ 46, 24, 33 },{ 46, 36, 8 }, },
+ { { 100, 21, 2423 },{ 66, 44, 328 },{ 54, 32, 33 },{ 54, 44, 8 }, }, { { 86, 7, 1253 },{ 74, 52, 328 },{ 62, 40, 33 },{ 62, 52, 8 }, },
+ { { 95, 16, 1253 },{ 83, 61, 328 },{ 71, 49, 33 },{ 71, 61, 8 }, }, { { 103, 24, 1253 },{ 91, 69, 328 },{ 79, 57, 33 },{ 79, 69, 8 }, },
+ { { 111, 32, 1253 },{ 99, 77, 328 },{ 87, 65, 33 },{ 87, 77, 8 }, }, { { 119, 40, 1253 },{ 107, 85, 328 },{ 95, 73, 33 },{ 95, 85, 8 }, },
+ { { 128, 49, 1253 },{ 116, 94, 328 },{ 104, 82, 33 },{ 104, 94, 8 }, }, { { 136, 57, 1253 },{ 124, 102, 328 },{ 112, 90, 33 },{ 112, 102, 8 }, },
+ { { 144, 65, 1253 },{ 132, 110, 328 },{ 120, 98, 33 },{ 120, 110, 8 }, }, { { 152, 73, 1253 },{ 140, 118, 328 },{ 128, 106, 33 },{ 128, 118, 8 }, },
+ { { 161, 82, 1253 },{ 149, 127, 328 },{ 137, 115, 33 },{ 137, 127, 8 }, }, { { 169, 90, 1253 },{ 157, 135, 328 },{ 145, 123, 33 },{ 145, 135, 8 }, },
+ { { 177, 98, 1253 },{ 165, 143, 328 },{ 153, 131, 33 },{ 153, 143, 8 }, }, { { 185, 106, 1253 },{ 173, 151, 328 },{ 161, 139, 33 },{ 161, 151, 8 }, },
+ { { 194, 115, 1253 },{ 182, 160, 328 },{ 170, 148, 33 },{ 170, 160, 8 }, }, { { 202, 123, 1253 },{ 190, 168, 328 },{ 178, 156, 33 },{ 178, 168, 8 }, },
+ { { 210, 131, 1253 },{ 198, 176, 328 },{ 186, 164, 33 },{ 186, 176, 8 }, }, { { 218, 139, 1253 },{ 206, 184, 328 },{ 194, 172, 33 },{ 194, 184, 8 }, },
+ { { 227, 148, 1253 },{ 215, 193, 328 },{ 203, 181, 33 },{ 203, 193, 8 }, }, { { 235, 156, 1253 },{ 223, 201, 328 },{ 211, 189, 33 },{ 211, 201, 8 }, },
+ { { 243, 164, 1253 },{ 231, 209, 328 },{ 219, 197, 33 },{ 219, 209, 8 }, }, { { 183, 239, 867 },{ 239, 217, 328 },{ 227, 205, 33 },{ 227, 217, 8 }, },
+ { { 254, 214, 1329 },{ 248, 226, 328 },{ 236, 214, 33 },{ 236, 226, 8 }, }, { { 222, 244, 3680 },{ 234, 244, 3648 },{ 244, 222, 33 },{ 244, 234, 8 }, },
+ { { 230, 252, 3680 },{ 242, 252, 3648 },{ 252, 230, 33 },{ 252, 242, 8 }, }, { { 238, 250, 4040 },{ 255, 250, 8 },{ 238, 250, 456 },{ 255, 250, 8 }, },
+ { { 9, 29, 566 },{ 9, 29, 560 },{ 9, 0, 9 },{ 9, 0, 8 }, }, { { 17, 37, 566 },{ 17, 37, 560 },{ 17, 0, 9 },{ 17, 0, 8 }, },
+ { { 45, 0, 313 },{ 45, 0, 312 },{ 25, 0, 49 },{ 25, 7, 8 }, }, { { 14, 63, 2758 },{ 5, 53, 784 },{ 15, 33, 70 },{ 33, 15, 8 }, },
+ { { 71, 6, 1329 },{ 72, 4, 1328 },{ 42, 4, 33 },{ 42, 24, 8 }, }, { { 70, 3, 239 },{ 70, 2, 232 },{ 50, 12, 33 },{ 50, 32, 8 }, },
+ { { 0, 98, 2842 },{ 78, 10, 232 },{ 58, 20, 33 },{ 58, 40, 8 }, }, { { 97, 27, 1329 },{ 86, 18, 232 },{ 66, 28, 33 },{ 66, 48, 8 }, },
+ { { 0, 94, 867 },{ 95, 27, 232 },{ 75, 37, 33 },{ 75, 57, 8 }, }, { { 8, 102, 867 },{ 103, 35, 232 },{ 83, 45, 33 },{ 83, 65, 8 }, },
+ { { 12, 112, 867 },{ 111, 43, 232 },{ 91, 53, 33 },{ 91, 73, 8 }, }, { { 139, 2, 1253 },{ 119, 51, 232 },{ 99, 61, 33 },{ 99, 81, 8 }, },
+ { { 148, 13, 1253 },{ 128, 60, 232 },{ 108, 70, 33 },{ 108, 90, 8 }, }, { { 156, 21, 1253 },{ 136, 68, 232 },{ 116, 78, 33 },{ 116, 98, 8 }, },
+ { { 164, 29, 1253 },{ 144, 76, 232 },{ 124, 86, 33 },{ 124, 106, 8 }, }, { { 172, 37, 1253 },{ 152, 84, 232 },{ 132, 94, 33 },{ 132, 114, 8 }, },
+ { { 181, 46, 1253 },{ 161, 93, 232 },{ 141, 103, 33 },{ 141, 123, 8 }, }, { { 189, 54, 1253 },{ 169, 101, 232 },{ 149, 111, 33 },{ 149, 131, 8 }, },
+ { { 197, 62, 1253 },{ 177, 109, 232 },{ 157, 119, 33 },{ 157, 139, 8 }, }, { { 205, 70, 1253 },{ 185, 117, 232 },{ 165, 127, 33 },{ 165, 147, 8 }, },
+ { { 214, 79, 1253 },{ 194, 126, 232 },{ 174, 136, 33 },{ 174, 156, 8 }, }, { { 222, 87, 1253 },{ 202, 134, 232 },{ 182, 144, 33 },{ 182, 164, 8 }, },
+ { { 230, 95, 1253 },{ 210, 142, 232 },{ 190, 152, 33 },{ 190, 172, 8 }, }, { { 238, 103, 1253 },{ 218, 150, 232 },{ 198, 160, 33 },{ 198, 180, 8 }, },
+ { { 247, 112, 1253 },{ 227, 159, 232 },{ 207, 169, 33 },{ 207, 189, 8 }, }, { { 255, 120, 1253 },{ 235, 167, 232 },{ 215, 177, 33 },{ 215, 197, 8 }, },
+ { { 146, 243, 867 },{ 243, 175, 232 },{ 223, 185, 33 },{ 223, 205, 8 }, }, { { 184, 231, 3682 },{ 203, 251, 784 },{ 231, 193, 33 },{ 231, 213, 8 }, },
+ { { 193, 240, 3682 },{ 222, 240, 3648 },{ 240, 202, 33 },{ 240, 222, 8 }, }, { { 255, 210, 169 },{ 230, 248, 3648 },{ 248, 210, 33 },{ 248, 230, 8 }, },
+ { { 218, 238, 4040 },{ 255, 238, 8 },{ 218, 238, 456 },{ 255, 238, 8 }, }, { { 226, 246, 4040 },{ 255, 246, 8 },{ 226, 246, 456 },{ 255, 246, 8 }, },
+ { { 13, 42, 566 },{ 13, 42, 560 },{ 13, 0, 9 },{ 13, 0, 8 }, }, { { 50, 0, 329 },{ 50, 0, 328 },{ 21, 0, 9 },{ 21, 0, 8 }, },
+ { { 29, 58, 566 },{ 67, 2, 1352 },{ 3, 29, 70 },{ 29, 3, 8 }, }, { { 10, 79, 2758 },{ 76, 11, 1352 },{ 11, 37, 70 },{ 37, 11, 8 }, },
+ { { 7, 75, 790 },{ 7, 75, 784 },{ 20, 46, 70 },{ 46, 20, 8 }, }, { { 15, 83, 790 },{ 97, 1, 1328 },{ 28, 54, 70 },{ 54, 28, 8 }, },
+ { { 101, 7, 1329 },{ 105, 9, 1328 },{ 62, 0, 39 },{ 62, 36, 8 }, }, { { 99, 1, 239 },{ 99, 3, 232 },{ 1, 71, 98 },{ 70, 44, 8 }, },
+ { { 107, 11, 239 },{ 108, 12, 232 },{ 10, 80, 98 },{ 79, 53, 8 }, }, { { 115, 19, 239 },{ 116, 20, 232 },{ 18, 88, 98 },{ 87, 61, 8 }, },
+ { { 123, 27, 239 },{ 124, 28, 232 },{ 26, 96, 98 },{ 95, 69, 8 }, }, { { 131, 35, 239 },{ 132, 36, 232 },{ 34, 104, 98 },{ 103, 77, 8 }, },
+ { { 140, 44, 239 },{ 141, 45, 232 },{ 43, 113, 98 },{ 112, 86, 8 }, }, { { 148, 52, 239 },{ 149, 53, 232 },{ 51, 121, 98 },{ 120, 94, 8 }, },
+ { { 156, 60, 239 },{ 157, 61, 232 },{ 59, 129, 98 },{ 128, 102, 8 }, }, { { 164, 68, 239 },{ 165, 69, 232 },{ 67, 137, 98 },{ 136, 110, 8 }, },
+ { { 173, 77, 239 },{ 174, 78, 232 },{ 76, 146, 98 },{ 145, 119, 8 }, }, { { 181, 85, 239 },{ 182, 86, 232 },{ 84, 154, 98 },{ 153, 127, 8 }, },
+ { { 189, 93, 239 },{ 190, 94, 232 },{ 92, 162, 98 },{ 161, 135, 8 }, }, { { 197, 101, 239 },{ 198, 102, 232 },{ 100, 170, 98 },{ 169, 143, 8 }, },
+ { { 206, 110, 239 },{ 207, 111, 232 },{ 109, 179, 98 },{ 178, 152, 8 }, }, { { 214, 118, 239 },{ 215, 119, 232 },{ 117, 187, 98 },{ 186, 160, 8 }, },
+ { { 222, 126, 239 },{ 223, 127, 232 },{ 125, 195, 98 },{ 194, 168, 8 }, }, { { 230, 134, 239 },{ 231, 135, 232 },{ 133, 203, 98 },{ 202, 176, 8 }, },
+ { { 239, 143, 239 },{ 240, 144, 232 },{ 142, 212, 98 },{ 211, 185, 8 }, }, { { 247, 151, 239 },{ 180, 248, 784 },{ 150, 220, 98 },{ 219, 193, 8 }, },
+ { { 159, 228, 3682 },{ 201, 227, 3648 },{ 158, 228, 98 },{ 227, 201, 8 }, }, { { 181, 249, 3928 },{ 209, 235, 3648 },{ 166, 236, 98 },{ 235, 209, 8 }, },
+ { { 255, 189, 169 },{ 218, 244, 3648 },{ 175, 245, 98 },{ 244, 218, 8 }, }, { { 197, 226, 4040 },{ 226, 252, 3648 },{ 183, 253, 98 },{ 252, 226, 8 }, },
+ { { 205, 234, 4040 },{ 255, 234, 8 },{ 205, 234, 456 },{ 255, 234, 8 }, }, { { 213, 242, 4040 },{ 255, 242, 8 },{ 213, 242, 456 },{ 255, 242, 8 }, },
+ { { 18, 60, 566 },{ 18, 60, 560 },{ 18, 0, 9 },{ 18, 0, 8 }, }, { { 26, 68, 566 },{ 26, 68, 560 },{ 26, 0, 9 },{ 26, 0, 8 }, },
+ { { 34, 76, 566 },{ 34, 76, 560 },{ 34, 0, 9 },{ 34, 0, 8 }, }, { { 5, 104, 2758 },{ 98, 5, 1352 },{ 42, 0, 57 },{ 42, 6, 8 }, },
+ { { 92, 0, 313 },{ 93, 1, 312 },{ 15, 51, 70 },{ 51, 15, 8 }, }, { { 3, 101, 790 },{ 3, 101, 784 },{ 0, 59, 88 },{ 59, 23, 8 }, },
+ { { 14, 107, 790 },{ 11, 109, 784 },{ 31, 67, 70 },{ 67, 31, 8 }, }, { { 19, 117, 790 },{ 19, 117, 784 },{ 39, 75, 70 },{ 75, 39, 8 }, },
+ { { 28, 126, 790 },{ 28, 126, 784 },{ 83, 5, 33 },{ 84, 48, 8 }, }, { { 132, 0, 239 },{ 36, 134, 784 },{ 91, 13, 33 },{ 92, 56, 8 }, },
+ { { 142, 4, 239 },{ 44, 142, 784 },{ 99, 21, 33 },{ 100, 64, 8 }, }, { { 150, 12, 239 },{ 52, 150, 784 },{ 107, 29, 33 },{ 108, 72, 8 }, },
+ { { 159, 21, 239 },{ 61, 159, 784 },{ 116, 38, 33 },{ 117, 81, 8 }, }, { { 167, 29, 239 },{ 69, 167, 784 },{ 124, 46, 33 },{ 125, 89, 8 }, },
+ { { 175, 37, 239 },{ 77, 175, 784 },{ 132, 54, 33 },{ 133, 97, 8 }, }, { { 183, 45, 239 },{ 85, 183, 784 },{ 140, 62, 33 },{ 141, 105, 8 }, },
+ { { 192, 54, 239 },{ 94, 192, 784 },{ 149, 71, 33 },{ 150, 114, 8 }, }, { { 200, 62, 239 },{ 102, 200, 784 },{ 157, 79, 33 },{ 158, 122, 8 }, },
+ { { 208, 70, 239 },{ 110, 208, 784 },{ 165, 87, 33 },{ 166, 130, 8 }, }, { { 216, 78, 239 },{ 118, 216, 784 },{ 173, 95, 33 },{ 174, 138, 8 }, },
+ { { 225, 87, 239 },{ 127, 225, 784 },{ 182, 104, 33 },{ 183, 147, 8 }, }, { { 233, 95, 239 },{ 135, 233, 784 },{ 190, 112, 33 },{ 191, 155, 8 }, },
+ { { 241, 103, 239 },{ 143, 241, 784 },{ 198, 120, 33 },{ 199, 163, 8 }, }, { { 111, 208, 3682 },{ 151, 249, 784 },{ 206, 128, 33 },{ 207, 171, 8 }, },
+ { { 120, 217, 3682 },{ 180, 216, 3648 },{ 215, 137, 33 },{ 216, 180, 8 }, }, { { 128, 225, 3682 },{ 188, 224, 3648 },{ 223, 145, 33 },{ 224, 188, 8 }, },
+ { { 155, 253, 3928 },{ 196, 232, 3648 },{ 231, 153, 33 },{ 232, 196, 8 }, }, { { 144, 241, 3682 },{ 204, 240, 3648 },{ 239, 161, 33 },{ 240, 204, 8 }, },
+ { { 153, 250, 3682 },{ 213, 249, 3648 },{ 248, 170, 33 },{ 249, 213, 8 }, }, { { 179, 221, 4040 },{ 255, 221, 8 },{ 179, 221, 456 },{ 255, 221, 8 }, },
+ { { 187, 229, 4040 },{ 255, 229, 8 },{ 187, 229, 456 },{ 255, 229, 8 }, }, { { 195, 237, 4040 },{ 255, 237, 8 },{ 195, 237, 456 },{ 255, 237, 8 }, },
+ { { 24, 80, 566 },{ 24, 80, 560 },{ 24, 0, 9 },{ 24, 0, 8 }, }, { { 32, 88, 566 },{ 32, 88, 560 },{ 32, 0, 9 },{ 32, 0, 8 }, },
+ { { 40, 96, 566 },{ 40, 96, 560 },{ 40, 0, 9 },{ 40, 0, 8 }, }, { { 48, 104, 566 },{ 48, 104, 560 },{ 48, 0, 9 },{ 48, 0, 8 }, },
+ { { 9, 138, 2758 },{ 130, 7, 1352 },{ 9, 57, 70 },{ 57, 9, 8 }, }, { { 119, 0, 313 },{ 120, 0, 312 },{ 17, 65, 70 },{ 65, 17, 8 }, },
+ { { 0, 128, 784 },{ 128, 6, 312 },{ 25, 73, 70 },{ 73, 25, 8 }, }, { { 6, 137, 790 },{ 5, 136, 784 },{ 33, 81, 70 },{ 81, 33, 8 }, },
+ { { 42, 171, 2758 },{ 14, 145, 784 },{ 42, 90, 70 },{ 90, 42, 8 }, }, { { 50, 179, 2758 },{ 22, 153, 784 },{ 50, 98, 70 },{ 98, 50, 8 }, },
+ { { 58, 187, 2758 },{ 30, 161, 784 },{ 58, 106, 70 },{ 106, 58, 8 }, }, { { 191, 18, 1329 },{ 38, 169, 784 },{ 112, 9, 33 },{ 114, 66, 8 }, },
+ { { 176, 0, 239 },{ 47, 178, 784 },{ 121, 18, 33 },{ 123, 75, 8 }, }, { { 187, 1, 239 },{ 55, 186, 784 },{ 129, 26, 33 },{ 131, 83, 8 }, },
+ { { 195, 10, 239 },{ 63, 194, 784 },{ 137, 34, 33 },{ 139, 91, 8 }, }, { { 203, 18, 239 },{ 71, 202, 784 },{ 145, 42, 33 },{ 147, 99, 8 }, },
+ { { 212, 27, 239 },{ 80, 211, 784 },{ 154, 51, 33 },{ 156, 108, 8 }, }, { { 220, 35, 239 },{ 88, 219, 784 },{ 162, 59, 33 },{ 164, 116, 8 }, },
+ { { 228, 43, 239 },{ 96, 227, 784 },{ 170, 67, 33 },{ 172, 124, 8 }, }, { { 236, 51, 239 },{ 104, 235, 784 },{ 178, 75, 33 },{ 180, 132, 8 }, },
+ { { 245, 60, 239 },{ 113, 244, 784 },{ 187, 84, 33 },{ 189, 141, 8 }, }, { { 91, 194, 3680 },{ 149, 197, 3648 },{ 195, 92, 33 },{ 197, 149, 8 }, },
+ { { 99, 202, 3680 },{ 157, 205, 3648 },{ 203, 100, 33 },{ 205, 157, 8 }, }, { { 107, 210, 3680 },{ 165, 213, 3648 },{ 211, 108, 33 },{ 213, 165, 8 }, },
+ { { 119, 249, 3928 },{ 174, 222, 3648 },{ 220, 117, 33 },{ 222, 174, 8 }, }, { { 127, 255, 856 },{ 182, 230, 3648 },{ 228, 125, 33 },{ 230, 182, 8 }, },
+ { { 255, 135, 169 },{ 190, 238, 3648 },{ 236, 133, 33 },{ 238, 190, 8 }, }, { { 140, 243, 3680 },{ 198, 246, 3648 },{ 244, 141, 33 },{ 246, 198, 8 }, },
+ { { 151, 207, 4040 },{ 255, 207, 8 },{ 151, 207, 456 },{ 255, 207, 8 }, }, { { 159, 215, 4040 },{ 255, 215, 8 },{ 159, 215, 456 },{ 255, 215, 8 }, },
+ { { 167, 223, 4040 },{ 255, 223, 8 },{ 167, 223, 456 },{ 255, 223, 8 }, }, { { 175, 231, 4040 },{ 255, 231, 8 },{ 175, 231, 456 },{ 255, 231, 8 }, },
+ { { 33, 106, 566 },{ 33, 106, 560 },{ 33, 0, 9 },{ 33, 0, 8 }, }, { { 41, 114, 566 },{ 41, 114, 560 },{ 41, 0, 9 },{ 41, 0, 8 }, },
+ { { 49, 122, 566 },{ 49, 122, 560 },{ 49, 0, 9 },{ 49, 0, 8 }, }, { { 57, 130, 566 },{ 57, 130, 560 },{ 57, 0, 9 },{ 57, 0, 8 }, },
+ { { 66, 139, 566 },{ 66, 139, 560 },{ 66, 0, 9 },{ 66, 0, 8 }, }, { { 74, 147, 566 },{ 170, 7, 1352 },{ 8, 74, 70 },{ 74, 8, 8 }, },
+ { { 152, 0, 313 },{ 178, 15, 1352 },{ 0, 82, 80 },{ 82, 16, 8 }, }, { { 162, 0, 313 },{ 186, 23, 1352 },{ 24, 90, 70 },{ 90, 24, 8 }, },
+ { { 0, 171, 784 },{ 195, 32, 1352 },{ 33, 99, 70 },{ 99, 33, 8 }, }, { { 6, 179, 790 },{ 203, 40, 1352 },{ 41, 107, 70 },{ 107, 41, 8 }, },
+ { { 15, 187, 790 },{ 211, 48, 1352 },{ 115, 0, 41 },{ 115, 49, 8 }, }, { { 61, 199, 710 },{ 219, 56, 1352 },{ 57, 123, 70 },{ 123, 57, 8 }, },
+ { { 70, 208, 710 },{ 228, 65, 1352 },{ 66, 132, 70 },{ 132, 66, 8 }, }, { { 78, 216, 710 },{ 236, 73, 1352 },{ 74, 140, 70 },{ 140, 74, 8 }, },
+ { { 86, 224, 710 },{ 244, 81, 1352 },{ 145, 7, 33 },{ 148, 82, 8 }, }, { { 222, 8, 233 },{ 252, 89, 1352 },{ 153, 15, 33 },{ 156, 90, 8 }, },
+ { { 235, 0, 239 },{ 241, 101, 328 },{ 166, 6, 39 },{ 165, 99, 8 }, }, { { 32, 170, 3680 },{ 249, 109, 328 },{ 0, 175, 98 },{ 173, 107, 8 }, },
+ { { 40, 178, 3680 },{ 115, 181, 3648 },{ 8, 183, 98 },{ 181, 115, 8 }, }, { { 48, 186, 3680 },{ 123, 189, 3648 },{ 16, 191, 98 },{ 189, 123, 8 }, },
+ { { 57, 195, 3680 },{ 132, 198, 3648 },{ 25, 200, 98 },{ 198, 132, 8 }, }, { { 67, 243, 3928 },{ 140, 206, 3648 },{ 33, 208, 98 },{ 206, 140, 8 }, },
+ { { 76, 251, 3928 },{ 148, 214, 3648 },{ 41, 216, 98 },{ 214, 148, 8 }, }, { { 86, 255, 856 },{ 156, 222, 3648 },{ 49, 224, 98 },{ 222, 156, 8 }, },
+ { { 255, 93, 169 },{ 165, 231, 3648 },{ 58, 233, 98 },{ 231, 165, 8 }, }, { { 98, 236, 3680 },{ 173, 239, 3648 },{ 66, 241, 98 },{ 239, 173, 8 }, },
+ { { 108, 181, 4040 },{ 181, 247, 3648 },{ 74, 249, 98 },{ 247, 181, 8 }, }, { { 116, 189, 4040 },{ 255, 189, 8 },{ 116, 189, 456 },{ 255, 189, 8 }, },
+ { { 125, 198, 4040 },{ 255, 198, 8 },{ 125, 198, 456 },{ 255, 198, 8 }, }, { { 133, 206, 4040 },{ 255, 206, 8 },{ 133, 206, 456 },{ 255, 206, 8 }, },
+ { { 141, 214, 4040 },{ 255, 214, 8 },{ 141, 214, 456 },{ 255, 214, 8 }, }, { { 149, 222, 4040 },{ 255, 222, 8 },{ 149, 222, 456 },{ 255, 222, 8 }, },
+ { { 47, 183, 566 },{ 47, 183, 560 },{ 47, 0, 9 },{ 47, 0, 8 }, }, { { 55, 191, 566 },{ 55, 191, 560 },{ 55, 0, 9 },{ 55, 0, 8 }, },
+ { { 63, 199, 566 },{ 63, 199, 560 },{ 63, 0, 9 },{ 63, 0, 8 }, }, { { 71, 207, 566 },{ 71, 207, 560 },{ 71, 0, 9 },{ 71, 0, 8 }, },
+ { { 80, 216, 566 },{ 80, 216, 560 },{ 80, 0, 9 },{ 80, 0, 8 }, }, { { 88, 224, 566 },{ 88, 224, 560 },{ 88, 0, 9 },{ 88, 0, 8 }, },
+ { { 3, 233, 710 },{ 3, 233, 704 },{ 2, 96, 70 },{ 96, 2, 8 }, }, { { 11, 241, 710 },{ 11, 241, 704 },{ 10, 104, 70 },{ 104, 10, 8 }, },
+ { { 20, 250, 710 },{ 20, 250, 704 },{ 19, 113, 70 },{ 113, 19, 8 }, }, { { 27, 121, 3654 },{ 27, 121, 3648 },{ 27, 121, 70 },{ 121, 27, 8 }, },
+ { { 35, 129, 3654 },{ 35, 129, 3648 },{ 35, 129, 70 },{ 129, 35, 8 }, }, { { 43, 137, 3654 },{ 43, 137, 3648 },{ 43, 137, 70 },{ 137, 43, 8 }, },
+ { { 52, 146, 3654 },{ 52, 146, 3648 },{ 52, 146, 70 },{ 146, 52, 8 }, }, { { 60, 154, 3654 },{ 60, 154, 3648 },{ 60, 154, 70 },{ 154, 60, 8 }, },
+ { { 68, 162, 3654 },{ 68, 162, 3648 },{ 68, 162, 70 },{ 162, 68, 8 }, }, { { 76, 170, 3654 },{ 76, 170, 3648 },{ 76, 170, 70 },{ 170, 76, 8 }, },
+ { { 85, 179, 3654 },{ 85, 179, 3648 },{ 85, 179, 70 },{ 179, 85, 8 }, }, { { 93, 187, 3654 },{ 93, 187, 3648 },{ 93, 187, 70 },{ 187, 93, 8 }, },
+ { { 101, 195, 3654 },{ 101, 195, 3648 },{ 101, 195, 70 },{ 195, 101, 8 }, }, { { 109, 203, 3654 },{ 109, 203, 3648 },{ 109, 203, 70 },{ 203, 109, 8 }, },
+ { { 118, 212, 3654 },{ 118, 212, 3648 },{ 118, 212, 70 },{ 212, 118, 8 }, }, { { 126, 220, 3654 },{ 126, 220, 3648 },{ 126, 220, 70 },{ 220, 126, 8 }, },
+ { { 134, 228, 3654 },{ 134, 228, 3648 },{ 134, 228, 70 },{ 228, 134, 8 }, }, { { 5, 236, 3680 },{ 142, 236, 3648 },{ 5, 236, 96 },{ 236, 142, 8 }, },
+ { { 14, 245, 3680 },{ 151, 245, 3648 },{ 14, 245, 96 },{ 245, 151, 8 }, }, { { 23, 159, 4040 },{ 159, 253, 3648 },{ 23, 159, 456 },{ 253, 159, 8 }, },
+ { { 31, 167, 4040 },{ 255, 167, 8 },{ 31, 167, 456 },{ 255, 167, 8 }, }, { { 39, 175, 4040 },{ 255, 175, 8 },{ 39, 175, 456 },{ 255, 175, 8 }, },
+ { { 48, 184, 4040 },{ 255, 184, 8 },{ 48, 184, 456 },{ 255, 184, 8 }, }, { { 56, 192, 4040 },{ 255, 192, 8 },{ 56, 192, 456 },{ 255, 192, 8 }, },
+ { { 64, 200, 4040 },{ 255, 200, 8 },{ 64, 200, 456 },{ 255, 200, 8 }, },{ { 72, 208, 4040 },{ 255, 208, 8 },{ 72, 208, 456 },{ 255, 208, 8 }, },
+
+ };
+
+ struct dxt5a_block
+ {
+ uint8_t m_endpoints[2];
+
+ enum { cTotalSelectorBytes = 6 };
+ uint8_t m_selectors[cTotalSelectorBytes];
+
+ inline void clear()
+ {
+ basisu::clear_obj(*this);
+ }
+
+ inline uint32_t get_low_alpha() const
+ {
+ return m_endpoints[0];
+ }
+
+ inline uint32_t get_high_alpha() const
+ {
+ return m_endpoints[1];
+ }
+
+ inline void set_low_alpha(uint32_t i)
+ {
+ assert(i <= UINT8_MAX);
+ m_endpoints[0] = static_cast