From ca241db6276a5d520aa9c46ed57be3c66319ca1e Mon Sep 17 00:00:00 2001 From: dolcetriade Date: Tue, 3 Jul 2018 22:49:58 -0700 Subject: [PATCH] image: add crn support to the image plugin. This works by converting the crn file to dds in memory and then using the dds load functions to convert the dds file to RGBA. This is because crn also stores the file as a compressed texture meant to be uploaded directly to the GPU, however, radiant wants an RGBA array. --- libs/CMakeLists.txt | 1 + libs/crunch/CMakeLists.txt | 9 + libs/crunch/crn_decomp.h | 3787 ++++++++++++++++++++++++++++++++++ libs/crunch/crn_defs.h | 291 +++ libs/crunch/crn_rgba.cpp | 128 ++ libs/crunch/crn_rgba.h | 35 + libs/crunch/crnlib.h | 641 ++++++ plugins/image/CMakeLists.txt | 6 +- plugins/image/crn.cpp | 55 + plugins/image/crn.h | 32 + plugins/image/image.cpp | 25 + 11 files changed, 5008 insertions(+), 2 deletions(-) create mode 100644 libs/crunch/CMakeLists.txt create mode 100644 libs/crunch/crn_decomp.h create mode 100644 libs/crunch/crn_defs.h create mode 100644 libs/crunch/crn_rgba.cpp create mode 100644 libs/crunch/crn_rgba.h create mode 100644 libs/crunch/crnlib.h create mode 100644 plugins/image/crn.cpp create mode 100644 plugins/image/crn.h diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt index 9f5f834c..d4b04756 100644 --- a/libs/CMakeLists.txt +++ b/libs/CMakeLists.txt @@ -1,5 +1,6 @@ add_subdirectory(cmdlib) add_subdirectory(container) +add_subdirectory(crunch) add_subdirectory(ddslib) add_subdirectory(debugging) add_subdirectory(etclib) diff --git a/libs/crunch/CMakeLists.txt b/libs/crunch/CMakeLists.txt new file mode 100644 index 00000000..8d6d16bf --- /dev/null +++ b/libs/crunch/CMakeLists.txt @@ -0,0 +1,9 @@ +add_library(crnlib + crn_decomp.h + crnlib.h + crn_rgba.h + crn_rgba.cpp +) +set_target_properties(crnlib PROPERTIES LINKER_LANGUAGE CXX) +target_link_libraries(crnlib PRIVATE ddslib) +target_compile_options(crnlib PRIVATE -fexceptions) diff --git a/libs/crunch/crn_decomp.h b/libs/crunch/crn_decomp.h new file mode 100644 index 00000000..d3732d6d --- /dev/null +++ b/libs/crunch/crn_decomp.h @@ -0,0 +1,3787 @@ +// File: crn_decomp.h - Fast CRN->DXTc texture transcoder header file library +// Copyright (c) 2010-2016 Richard Geldreich, Jr. and Binomial LLC +// See Copyright Notice and license at the end of this file. +// +// This single header file contains *all* of the code necessary to unpack .CRN files to raw DXTn bits. +// It does NOT depend on the crn compression library. +// +// Note: This is a single file, stand-alone C++ library which is controlled by the use of the following macro: +// If CRND_INCLUDE_CRND_H is NOT defined, the header is included. +// +// Important: If compiling with gcc, be sure strict aliasing is disabled: -fno-strict-aliasing +#ifndef CRND_INCLUDE_CRND_H +#define CRND_INCLUDE_CRND_H + +// Include crn_defs.h (only to bring in some basic CRN-related types and structures). +#include "crn_defs.h" + +#include +#include +#ifdef WIN32 +#include +#elif defined(__APPLE__) +#include +#else +#include +#endif +#include +#include // needed for placement new, _msize, _expand +#include + +#define CRND_RESTRICT __restrict + +#ifdef _MSC_VER +#pragma warning(disable : 4127) // warning C4127: conditional expression is constant +#endif + +#ifdef CRND_DEVEL +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x500 +#endif +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef +#define NOMINMAX +#endif +#include "windows.h" // only for IsDebuggerPresent(), DebugBreak(), and OutputDebugStringA() +#endif + +// File: crnd_types.h +namespace crnd { +const crn_uint8 cUINT8_MIN = 0; +const crn_uint8 cUINT8_MAX = 0xFFU; +const uint16 cUINT16_MIN = 0; +const uint16 cUINT16_MAX = 0xFFFFU; +const uint32 cUINT32_MIN = 0; +const uint32 cUINT32_MAX = 0xFFFFFFFFU; + +const int8 cINT8_MIN = -128; +const int8 cINT8_MAX = 127; +const int16 cINT16_MIN = -32768; +const int16 cINT16_MAX = 32767; +const int32 cINT32_MIN = (-2147483647 - 1); +const int32 cINT32_MAX = 2147483647; + +enum eClear { cClear }; + +const uint32 cIntBits = 32U; + +template +struct int_traits { + enum { cMin = crnd::cINT32_MIN, + cMax = crnd::cINT32_MAX, + cSigned = true }; +}; + +template <> +struct int_traits { + enum { cMin = crnd::cINT8_MIN, + cMax = crnd::cINT8_MAX, + cSigned = true }; +}; +template <> +struct int_traits { + enum { cMin = crnd::cINT16_MIN, + cMax = crnd::cINT16_MAX, + cSigned = true }; +}; +template <> +struct int_traits { + enum { cMin = crnd::cINT32_MIN, + cMax = crnd::cINT32_MAX, + cSigned = true }; +}; + +template <> +struct int_traits { + enum { cMin = 0, + cMax = crnd::cUINT8_MAX, + cSigned = false }; +}; +template <> +struct int_traits { + enum { cMin = 0, + cMax = crnd::cUINT16_MAX, + cSigned = false }; +}; +template <> +struct int_traits { + enum { cMin = 0, + cMax = crnd::cUINT32_MAX, + cSigned = false }; +}; + +struct empty_type {}; + +} // namespace crnd + +// File: crnd_platform.h +namespace crnd { + +bool crnd_is_debugger_present(); +void crnd_debug_break(); +void crnd_output_debug_string(const char* p); + +// actually in crnd_assert.cpp +void crnd_assert(const char* pExp, const char* pFile, unsigned line); +void crnd_fail(const char* pExp, const char* pFile, unsigned line); + +} // namespace crnd + +// File: crnd_assert.h +namespace crnd { +class decompression_exception : public std::exception {}; + +// HACK: Try to crash less when asked to decode invalid inputs. +#define CRND_ASSERT(_exp) (!!(_exp) ? (void)0 : throw decompression_exception()) + +void crnd_trace(const char* pFmt, va_list args); +void crnd_trace(const char* pFmt, ...); + +} // namespace crnd + +// File: crnd_helpers.h +namespace crnd { +namespace helpers { +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)); } +}; + +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 +void construct_array(T* p, uint32 n) { + T* q = p + n; + for (; p != q; ++p) + new (static_cast(p)) T; +} + +template +void construct_array(T* p, uint32 n, const U& init) { + T* q = p + n; + for (; p != q; ++p) + new (static_cast(p)) T(init); +} + +template +inline void destruct(T* p) { + p->~T(); +} + +template +inline void destruct_array(T* p, uint32 n) { + T* q = p + n; + for (; p != q; ++p) + p->~T(); +} + +} // namespace helpers + +} // namespace crnd + +// File: crnd_traits.h +namespace crnd { +template +struct scalar_type { + enum { cFlag = false }; + static inline void construct(T* p) { helpers::construct(p); } + static inline void construct(T* p, const T& init) { helpers::construct(p, init); } + static inline void construct_array(T* p, uint32 n) { helpers::construct_array(p, n); } + static inline void destruct(T* p) { helpers::destruct(p); } + static inline void destruct_array(T* p, uint32 n) { helpers::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, uint32 n) { memset(p, 0, sizeof(T*) * n); } + static inline void destruct(T**) {} + static inline void destruct_array(T**, uint32) {} +}; + +#define CRND_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, uint32 n) { memset(p, 0, sizeof(X) * n); } \ + static inline void destruct(X*) {} \ + static inline void destruct_array(X*, uint32) {} \ + }; + +CRND_DEFINE_BUILT_IN_TYPE(bool) +CRND_DEFINE_BUILT_IN_TYPE(char) +CRND_DEFINE_BUILT_IN_TYPE(unsigned char) +CRND_DEFINE_BUILT_IN_TYPE(short) +CRND_DEFINE_BUILT_IN_TYPE(unsigned short) +CRND_DEFINE_BUILT_IN_TYPE(int) +CRND_DEFINE_BUILT_IN_TYPE(unsigned int) +CRND_DEFINE_BUILT_IN_TYPE(long) +CRND_DEFINE_BUILT_IN_TYPE(unsigned long) +CRND_DEFINE_BUILT_IN_TYPE(int64) +CRND_DEFINE_BUILT_IN_TYPE(uint64) +CRND_DEFINE_BUILT_IN_TYPE(float) +CRND_DEFINE_BUILT_IN_TYPE(double) +CRND_DEFINE_BUILT_IN_TYPE(long double) + +#undef CRND_DEFINE_BUILT_IN_TYPE + +// See: http://erdani.org/publications/cuj-2004-06.pdf + +template +struct bitwise_movable { + enum { cFlag = false }; +}; + +// Defines type Q as bitwise movable. +#define CRND_DEFINE_BITWISE_MOVABLE(Q) \ + template <> \ + struct bitwise_movable { \ + enum { cFlag = true }; \ + }; + +// From yasli_traits.h: +// Credit goes to Boost; +// also found in the C++ Templates book by Vandevoorde and Josuttis + +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 }; +}; + +#define CRND_IS_POD(T) __is_pod(T) + +} // namespace crnd + +// File: crnd_mem.h +namespace crnd { +void* crnd_malloc(size_t size, size_t* pActual_size = NULL); +void* crnd_realloc(void* p, size_t size, size_t* pActual_size = NULL, bool movable = true); +void crnd_free(void* p); +size_t crnd_msize(void* p); + +template +inline T* crnd_new() { + T* p = static_cast(crnd_malloc(sizeof(T))); + if (!p) + return NULL; + + return helpers::construct(p); +} + +template +inline T* crnd_new(const T& init) { + T* p = static_cast(crnd_malloc(sizeof(T))); + if (!p) + return NULL; + + return helpers::construct(p, init); +} + +template +inline T* crnd_new_array(uint32 num) { + if (!num) + num = 1; + + uint8* q = static_cast(crnd_malloc(CRND_MIN_ALLOC_ALIGNMENT + sizeof(T) * num)); + if (!q) + return NULL; + + T* p = reinterpret_cast(q + CRND_MIN_ALLOC_ALIGNMENT); + + reinterpret_cast(p)[-1] = num; + reinterpret_cast(p)[-2] = ~num; + + helpers::construct_array(p, num); + return p; +} + +template +inline void crnd_delete(T* p) { + if (p) { + helpers::destruct(p); + crnd_free(p); + } +} + +template +inline void crnd_delete_array(T* p) { + if (p) { + const uint32 num = reinterpret_cast(p)[-1]; + CRND_ASSERT(num && (num == ~reinterpret_cast(p)[-2])); + + helpers::destruct_array(p, num); + + crnd_free(reinterpret_cast(p) - CRND_MIN_ALLOC_ALIGNMENT); + } +} + +} // namespace crnd + +// File: crnd_math.h +namespace crnd { +namespace math { +const float cNearlyInfinite = 1.0e+37f; + +const float cDegToRad = 0.01745329252f; +const float cRadToDeg = 57.29577951f; + +extern uint32 g_bitmasks[32]; + +// Yes I know these should probably be pass by ref, not val: +// http://www.stepanovpapers.com/notes.pdf +// Just don't use them on non-simple (non built-in) types! +template +inline T minimum(T a, T b) { + return (a < b) ? a : b; +} + +template +inline T minimum(T a, T b, T c) { + return minimum(minimum(a, b), c); +} + +template +inline T maximum(T a, T b) { + return (a > b) ? a : b; +} + +template +inline T maximum(T a, T b, T c) { + return maximum(maximum(a, b), c); +} + +template +inline T clamp(T value, T low, T high) { + return (value < low) ? low : ((value > high) ? high : value); +} + +template +inline T square(T value) { + return value * value; +} + +inline bool is_power_of_2(uint32 x) { + return x && ((x & (x - 1U)) == 0U); +} + +// From "Hackers Delight" +inline int next_pow2(uint32 val) { + val--; + val |= val >> 16; + val |= val >> 8; + val |= val >> 4; + val |= val >> 2; + val |= val >> 1; + return val + 1; +} + +// Returns the total number of bits needed to encode v. +inline uint32 total_bits(uint32 v) { + uint32 l = 0; + while (v > 0U) { + v >>= 1; + l++; + } + return l; +} + +inline uint floor_log2i(uint v) { + uint l = 0; + while (v > 1U) { + v >>= 1; + l++; + } + return l; +} + +inline uint ceil_log2i(uint v) { + uint l = floor_log2i(v); + if ((l != cIntBits) && (v > (1U << l))) + l++; + return l; +} +} +} + +// File: crnd_utils.h +namespace crnd { +namespace utils { +template +inline void zero_object(T& obj) { + memset(&obj, 0, sizeof(obj)); +} + +template +inline void zero_this(T* pObj) { + memset(pObj, 0, sizeof(*pObj)); +} + +template +inline void swap(T& left, T& right) { + T temp(left); + left = right; + right = temp; +} + +inline void invert_buf(void* pBuf, uint32 size) { + uint8* p = static_cast(pBuf); + + const uint32 half_size = size >> 1; + for (uint32 i = 0; i < half_size; i++) + swap(p[i], p[size - 1U - i]); +} + +static inline uint16 swap16(uint16 x) { + return static_cast((x << 8) | (x >> 8)); +} +static inline uint32 swap32(uint32 x) { + return ((x << 24) | ((x << 8) & 0x00FF0000) | ((x >> 8) & 0x0000FF00) | (x >> 24)); +} + +uint32 compute_max_mips(uint32 width, uint32 height); + +} // namespace utils + +} // namespace crnd + +// File: crnd_vector.h +namespace crnd { +struct elemental_vector { + void* m_p; + uint32 m_size; + uint32 m_capacity; + + typedef void (*object_mover)(void* pDst, void* pSrc, uint32 num); + + bool increase_capacity(uint32 min_new_capacity, bool grow_hint, uint32 element_size, object_mover pRelocate); +}; + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4127) // warning C4127: conditional expression is constant +#endif + +template +class vector : public helpers::rel_ops > { + 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), + m_alloc_failed(false) { + } + + inline vector(const vector& other) + : m_p(NULL), + m_size(0), + m_capacity(0), + m_alloc_failed(false) { + *this = other; + } + + inline vector(uint32 size) + : m_p(NULL), + m_size(0), + m_capacity(0), + m_alloc_failed(false) { + resize(size); + } + + inline ~vector() { + clear(); + } + + // I don't like this. Not at all. But exceptions, or just failing suck worse. + inline bool get_alloc_failed() const { return m_alloc_failed; } + inline void clear_alloc_failed() { m_alloc_failed = false; } + + inline bool assign(const vector& other) { + if (this == &other) + return true; + + if (m_capacity == other.m_size) + resize(0); + else { + clear(); + + if (!increase_capacity(other.m_size, false)) + return false; + } + + if (scalar_type::cFlag) + memcpy(m_p, other.m_p, other.m_size * sizeof(T)); + else { + T* pDst = m_p; + const T* pSrc = other.m_p; + for (uint32 i = other.m_size; i > 0; i--) + helpers::construct(pDst++, *pSrc++); + } + + m_size = other.m_size; + + return true; + } + + inline vector& operator=(const vector& other) { + assign(other); + return *this; + } + + inline const T* begin() const { return m_p; } + T* begin() { return m_p; } + + inline const T* end() const { return m_p + m_size; } + T* end() { return m_p + m_size; } + + inline bool empty() const { return !m_size; } + inline uint32 size() const { return m_size; } + inline uint32 capacity() const { return m_capacity; } + + inline const T& operator[](uint32 i) const { + CRND_ASSERT(i < m_size); + return m_p[i]; + } + inline T& operator[](uint32 i) { + CRND_ASSERT(i < m_size); + return m_p[i]; + } + + inline const T& front() const { + CRND_ASSERT(m_size); + return m_p[0]; + } + inline T& front() { + CRND_ASSERT(m_size); + return m_p[0]; + } + + inline const T& back() const { + CRND_ASSERT(m_size); + return m_p[m_size - 1]; + } + inline T& back() { + CRND_ASSERT(m_size); + return m_p[m_size - 1]; + } + + inline void clear() { + if (m_p) { + scalar_type::destruct_array(m_p, m_size); + crnd_free(m_p); + m_p = NULL; + m_size = 0; + m_capacity = 0; + } + + m_alloc_failed = false; + } + + inline bool reserve(uint32 new_capacity) { + if (!increase_capacity(new_capacity, false)) + return false; + + return true; + } + + inline bool resize(uint32 new_size) { + 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))) + return false; + } + + scalar_type::construct_array(m_p + m_size, new_size - m_size); + } + + m_size = new_size; + } + + return true; + } + + inline bool push_back(const T& obj) { + CRND_ASSERT(!m_p || (&obj < m_p) || (&obj >= (m_p + m_size))); + + if (m_size >= m_capacity) { + if (!increase_capacity(m_size + 1, true)) + return false; + } + + scalar_type::construct(m_p + m_size, obj); + m_size++; + + return true; + } + + inline void pop_back() { + CRND_ASSERT(m_size); + + if (m_size) { + m_size--; + scalar_type::destruct(&m_p[m_size]); + } + } + + inline void insert(uint32 index, const T* p, uint32 n) { + CRND_ASSERT(index <= m_size); + if (!n) + return; + + const uint32 orig_size = m_size; + resize(m_size + n); + + const T* pSrc = m_p + orig_size - 1; + T* pDst = const_cast(pSrc) + n; + + const uint32 num_to_move = orig_size - index; + + for (uint32 i = 0; i < num_to_move; i++) { + CRND_ASSERT((pDst - m_p) < (int)m_size); + *pDst-- = *pSrc--; + } + + pSrc = p; + pDst = m_p + index; + + for (uint32 i = 0; i < n; i++) { + CRND_ASSERT((pDst - m_p) < (int)m_size); + *pDst++ = *p++; + } + } + + inline void erase(uint32 start, uint32 n) { + CRND_ASSERT((start + n) <= m_size); + + if (!n) + return; + + const uint32 num_to_move = m_size - (start + n); + + T* pDst = m_p + start; + T* pDst_end = pDst + num_to_move; + const T* pSrc = m_p + start + n; + + while (pDst != pDst_end) + *pDst++ = *pSrc++; + + scalar_type::destruct_array(pDst_end, n); + + m_size -= n; + } + + inline void erase(uint32 index) { + erase(index, 1); + } + + inline void erase(T* p) { + CRND_ASSERT((p >= m_p) && (p < (m_p + m_size))); + erase(p - m_p); + } + + 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 i = m_size; i; i--) + if (!(*pSrc++ == *pDst++)) + return false; + } + } + + return true; + } + + inline bool operator<(const vector& rhs) const { + const uint32 min_size = math::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; + } + + void swap(vector& other) { + utils::swap(m_p, other.m_p); + utils::swap(m_size, other.m_size); + utils::swap(m_capacity, other.m_capacity); + } + + private: + T* m_p; + uint32 m_size; + uint32 m_capacity; + bool m_alloc_failed; + + template + struct is_vector { + enum { cFlag = false }; + }; + template + struct is_vector > { + enum { cFlag = true }; + }; + + static void object_mover(void* pDst_void, void* pSrc_void, uint32 num) { + T* pSrc = static_cast(pSrc_void); + T* const pSrc_end = pSrc + num; + T* pDst = static_cast(pDst_void); + + while (pSrc != pSrc_end) { + helpers::construct(pDst, *pSrc); + pSrc->~T(); + pSrc++; + pDst++; + } + } + + inline bool increase_capacity(uint32 min_new_capacity, bool grow_hint) { + if (!reinterpret_cast(this)->increase_capacity( + min_new_capacity, grow_hint, sizeof(T), + ((scalar_type::cFlag) || (is_vector::cFlag) || (bitwise_movable::cFlag) || CRND_IS_POD(T)) ? NULL : object_mover)) { + m_alloc_failed = true; + return false; + } + return true; + } +}; + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +extern void vector_test(); + +} // namespace crnd + +// File: crnd_private.h +namespace crnd { +const crn_header* crnd_get_header(const void* pData, uint32 data_size); + +} // namespace crnd + +// File: checksum.h +namespace crnd { +// crc16() intended for small buffers - doesn't use an acceleration table. +const uint16 cInitCRC16 = 0; +uint16 crc16(const void* pBuf, uint32 len, uint16 crc = cInitCRC16); + +} // namespace crnd + +// File: crnd_color.h +namespace crnd { +template +struct color_quad_component_traits { + enum { + cSigned = false, + cFloat = false, + cMin = cUINT8_MIN, + cMax = cUINT8_MAX + }; +}; + +template <> +struct color_quad_component_traits { + enum { + cSigned = true, + cFloat = false, + cMin = cINT16_MIN, + cMax = cINT16_MAX + }; +}; + +template <> +struct color_quad_component_traits { + enum { + cSigned = false, + cFloat = false, + cMin = cUINT16_MIN, + cMax = cUINT16_MAX + }; +}; + +template <> +struct color_quad_component_traits { + enum { + cSigned = true, + cFloat = false, + cMin = cINT32_MIN, + cMax = cINT32_MAX + }; +}; + +template <> +struct color_quad_component_traits { + enum { + cSigned = false, + cFloat = false, + cMin = cUINT32_MIN, + cMax = cUINT32_MAX + }; +}; + +template <> +struct color_quad_component_traits { + enum { + cSigned = false, + cFloat = true, + cMin = cINT32_MIN, + cMax = cINT32_MAX + }; +}; + +template <> +struct color_quad_component_traits { + enum { + cSigned = false, + cFloat = true, + cMin = cINT32_MIN, + cMax = cINT32_MAX + }; +}; + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4201) // warning C4201: nonstandard extension used : nameless struct/union +#pragma warning(disable : 4127) // warning C4127: conditional expression is constant +#endif + +template +class color_quad : public helpers::rel_ops > { + static parameter_type clamp(parameter_type v) { + if (component_traits::cFloat) + return v; + else { + if (v < component_traits::cMin) + return component_traits::cMin; + else if (v > component_traits::cMax) + return component_traits::cMax; + return v; + } + } + + public: + typedef component_type component_t; + typedef parameter_type parameter_t; + typedef color_quad_component_traits component_traits; + + enum { cNumComps = 4 }; + + union { + struct + { + component_type r; + component_type g; + component_type b; + component_type a; + }; + + component_type c[cNumComps]; + }; + + inline color_quad() { + } + + inline color_quad(eClear) + : r(0), g(0), b(0), a(0) { + } + + inline color_quad(const color_quad& other) + : r(other.r), g(other.g), b(other.b), a(other.a) { + } + + inline color_quad(parameter_type y, parameter_type alpha = component_traits::cMax) { + set(y, alpha); + } + + inline color_quad(parameter_type red, parameter_type green, parameter_type blue, parameter_type alpha = component_traits::cMax) { + set(red, green, blue, alpha); + } + + template + inline color_quad(const color_quad& other) + : r(clamp(other.r)), g(clamp(other.g)), b(clamp(other.b)), a(clamp(other.a)) { + } + + inline void clear() { + r = 0; + g = 0; + b = 0; + a = 0; + } + + inline color_quad& operator=(const color_quad& other) { + r = other.r; + g = other.g; + b = other.b; + a = other.a; + return *this; + } + + template + inline color_quad& operator=(const color_quad& other) { + r = clamp(other.r); + g = clamp(other.g); + b = clamp(other.b); + a = clamp(other.a); + return *this; + } + + inline color_quad& set(parameter_type y, parameter_type alpha = component_traits::cMax) { + y = clamp(y); + r = static_cast(y); + g = static_cast(y); + b = static_cast(y); + a = static_cast(alpha); + return *this; + } + + inline color_quad& set(parameter_type red, parameter_type green, parameter_type blue, parameter_type alpha = component_traits::cMax) { + r = static_cast(clamp(red)); + g = static_cast(clamp(green)); + b = static_cast(clamp(blue)); + a = static_cast(clamp(alpha)); + return *this; + } + + inline color_quad& set_noclamp_rgba(parameter_type red, parameter_type green, parameter_type blue, parameter_type alpha) { + r = static_cast(red); + g = static_cast(green); + b = static_cast(blue); + a = static_cast(alpha); + return *this; + } + + inline color_quad& set_noclamp_rgb(parameter_type red, parameter_type green, parameter_type blue) { + r = static_cast(red); + g = static_cast(green); + b = static_cast(blue); + return *this; + } + + static inline parameter_type get_min_comp() { return component_traits::cMin; } + static inline parameter_type get_max_comp() { return component_traits::cMax; } + static inline bool get_comps_are_signed() { return component_traits::cSigned; } + + inline component_type operator[](uint32 i) const { + CRND_ASSERT(i < cNumComps); + return c[i]; + } + inline component_type& operator[](uint32 i) { + CRND_ASSERT(i < cNumComps); + return c[i]; + } + + inline color_quad& set_component(uint32 i, parameter_type f) { + CRND_ASSERT(i < cNumComps); + + c[i] = static_cast(clamp(f)); + + return *this; + } + + inline color_quad& clamp(const color_quad& l, const color_quad& h) { + for (uint32 i = 0; i < cNumComps; i++) + c[i] = static_cast(math::clamp(c[i], l[i], h[i])); + return *this; + } + + inline color_quad& clamp(parameter_type l, parameter_type h) { + for (uint32 i = 0; i < cNumComps; i++) + c[i] = static_cast(math::clamp(c[i], l, h)); + return *this; + } + + // Returns CCIR 601 luma (consistent with color_utils::RGB_To_Y). + inline parameter_type get_luma() const { + return static_cast((19595U * r + 38470U * g + 7471U * b + 32768) >> 16U); + } + + // Returns REC 709 luma. + inline parameter_type get_luma_rec709() const { + return static_cast((13938U * r + 46869U * g + 4729U * b + 32768U) >> 16U); + } + + inline uint32 squared_distance(const color_quad& c, bool alpha = true) const { + return math::square(r - c.r) + math::square(g - c.g) + math::square(b - c.b) + (alpha ? math::square(a - c.a) : 0); + } + + inline bool rgb_equals(const color_quad& rhs) const { + return (r == rhs.r) && (g == rhs.g) && (b == rhs.b); + } + + inline bool operator==(const color_quad& rhs) const { + return (r == rhs.r) && (g == rhs.g) && (b == rhs.b) && (a == rhs.a); + } + + inline bool operator<(const color_quad& rhs) const { + for (uint32 i = 0; i < cNumComps; i++) { + if (c[i] < rhs.c[i]) + return true; + else if (!(c[i] == rhs.c[i])) + return false; + } + return false; + } + + inline color_quad& operator+=(const color_quad& other) { + for (uint32 i = 0; i < 4; i++) + c[i] = static_cast(clamp(c[i] + other.c[i])); + return *this; + } + + inline color_quad& operator-=(const color_quad& other) { + for (uint32 i = 0; i < 4; i++) + c[i] = static_cast(clamp(c[i] - other.c[i])); + return *this; + } + + inline color_quad& operator*=(parameter_type v) { + for (uint32 i = 0; i < 4; i++) + c[i] = static_cast(clamp(c[i] * v)); + return *this; + } + + inline color_quad& operator/=(parameter_type v) { + for (uint32 i = 0; i < 4; i++) + c[i] = static_cast(c[i] / v); + return *this; + } + + inline color_quad get_swizzled(uint32 x, uint32 y, uint32 z, uint32 w) const { + CRND_ASSERT((x | y | z | w) < 4); + return color_quad(c[x], c[y], c[z], c[w]); + } + + inline friend color_quad operator+(const color_quad& lhs, const color_quad& rhs) { + color_quad result(lhs); + result += rhs; + return result; + } + + inline friend color_quad operator-(const color_quad& lhs, const color_quad& rhs) { + color_quad result(lhs); + result -= rhs; + return result; + } + + inline friend color_quad operator*(const color_quad& lhs, parameter_type v) { + color_quad result(lhs); + result *= v; + return result; + } + + friend inline color_quad operator/(const color_quad& lhs, parameter_type v) { + color_quad result(lhs); + result /= v; + return result; + } + + friend inline color_quad operator*(parameter_type v, const color_quad& rhs) { + color_quad result(rhs); + result *= v; + return result; + } + + inline uint32 get_min_component_index(bool alpha = true) const { + uint32 index = 0; + uint32 limit = alpha ? cNumComps : (cNumComps - 1); + for (uint32 i = 1; i < limit; i++) + if (c[i] < c[index]) + index = i; + return index; + } + + inline uint32 get_max_component_index(bool alpha = true) const { + uint32 index = 0; + uint32 limit = alpha ? cNumComps : (cNumComps - 1); + for (uint32 i = 1; i < limit; i++) + if (c[i] > c[index]) + index = i; + return index; + } + + inline void get_float4(float* pDst) { + for (uint32 i = 0; i < 4; i++) + pDst[i] = ((*this)[i] - component_traits::cMin) / float(component_traits::cMax - component_traits::cMin); + } + + inline void get_float3(float* pDst) { + for (uint32 i = 0; i < 3; i++) + pDst[i] = ((*this)[i] - component_traits::cMin) / float(component_traits::cMax - component_traits::cMin); + } + + static inline color_quad make_black() { + return color_quad(0, 0, 0, component_traits::cMax); + } + + static inline color_quad make_white() { + return color_quad(component_traits::cMax, component_traits::cMax, component_traits::cMax, component_traits::cMax); + } +}; // class color_quad + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +template +struct scalar_type > { + enum { cFlag = true }; + static inline void construct(color_quad* p) {} + static inline void construct(color_quad* p, const color_quad& init) { memcpy(p, &init, sizeof(color_quad)); } + static inline void construct_array(color_quad* p, uint32 n) { p, n; } + static inline void destruct(color_quad* p) { p; } + static inline void destruct_array(color_quad* p, uint32 n) { p, n; } +}; + +typedef color_quad color_quad_u8; +typedef color_quad color_quad_i16; +typedef color_quad color_quad_u16; +typedef color_quad color_quad_i32; +typedef color_quad color_quad_u32; +typedef color_quad color_quad_f; +typedef color_quad color_quad_d; + +} // namespace crnd + +// File: crnd_dxt.h +namespace crnd { +enum dxt_format { + cDXTInvalid = -1, + + // cDXT1/1A must appear first! + cDXT1, + cDXT1A, + + cDXT3, + cDXT5, + cDXT5A, + + cDXN_XY, // inverted relative to standard ATI2, 360's DXN + cDXN_YX // standard ATI2 +}; + +enum dxt_constants { + cDXTBlockShift = 2U, + cDXTBlockSize = 1U << cDXTBlockShift, + + cDXT1BytesPerBlock = 8U, + cDXT5NBytesPerBlock = 16U, + + cDXT1SelectorBits = 2U, + cDXT1SelectorValues = 1U << cDXT1SelectorBits, + cDXT1SelectorMask = cDXT1SelectorValues - 1U, + + cDXT5SelectorBits = 3U, + cDXT5SelectorValues = 1U << cDXT5SelectorBits, + cDXT5SelectorMask = cDXT5SelectorValues - 1U +}; + +const float cDXT1MaxLinearValue = 3.0f; +const float cDXT1InvMaxLinearValue = 1.0f / 3.0f; + +const float cDXT5MaxLinearValue = 7.0f; +const float cDXT5InvMaxLinearValue = 1.0f / 7.0f; + +// Converts DXT1 raw color selector index to a linear value. +extern const uint8 g_dxt1_to_linear[cDXT1SelectorValues]; + +// Converts DXT5 raw alpha selector index to a linear value. +extern const uint8 g_dxt5_to_linear[cDXT5SelectorValues]; + +// Converts DXT1 linear color selector index to a raw value (inverse of g_dxt1_to_linear). +extern const uint8 g_dxt1_from_linear[cDXT1SelectorValues]; + +// Converts DXT5 linear alpha selector index to a raw value (inverse of g_dxt5_to_linear). +extern const uint8 g_dxt5_from_linear[cDXT5SelectorValues]; + +extern const uint8 g_six_alpha_invert_table[cDXT5SelectorValues]; +extern const uint8 g_eight_alpha_invert_table[cDXT5SelectorValues]; + +struct dxt1_block { + uint8 m_low_color[2]; + uint8 m_high_color[2]; + + enum { cNumSelectorBytes = 4 }; + uint8 m_selectors[cNumSelectorBytes]; + + inline void clear() { + utils::zero_this(this); + } + + // These methods assume the in-memory rep is in LE byte order. + inline uint32 get_low_color() const { + return m_low_color[0] | (m_low_color[1] << 8U); + } + + inline uint32 get_high_color() const { + return m_high_color[0] | (m_high_color[1] << 8U); + } + + inline void set_low_color(uint16 c) { + m_low_color[0] = static_cast(c & 0xFF); + m_low_color[1] = static_cast((c >> 8) & 0xFF); + } + + inline void set_high_color(uint16 c) { + m_high_color[0] = static_cast(c & 0xFF); + m_high_color[1] = static_cast((c >> 8) & 0xFF); + } + + inline uint32 get_selector(uint32 x, uint32 y) const { + CRND_ASSERT((x < 4U) && (y < 4U)); + return (m_selectors[y] >> (x * cDXT1SelectorBits)) & cDXT1SelectorMask; + } + + inline void set_selector(uint32 x, uint32 y, uint32 val) { + CRND_ASSERT((x < 4U) && (y < 4U) && (val < 4U)); + + m_selectors[y] &= (~(cDXT1SelectorMask << (x * cDXT1SelectorBits))); + m_selectors[y] |= (val << (x * cDXT1SelectorBits)); + } + + static uint16 pack_color(const color_quad_u8& color, bool scaled, uint32 bias = 127U); + static uint16 pack_color(uint32 r, uint32 g, uint32 b, bool scaled, uint32 bias = 127U); + + static color_quad_u8 unpack_color(uint16 packed_color, bool scaled, uint32 alpha = 255U); + static void unpack_color(uint32& r, uint32& g, uint32& b, uint16 packed_color, bool scaled); + + static uint32 get_block_colors3(color_quad_u8* pDst, uint16 color0, uint16 color1); + static uint32 get_block_colors4(color_quad_u8* pDst, uint16 color0, uint16 color1); + // pDst must point to an array at least cDXT1SelectorValues long. + static uint32 get_block_colors(color_quad_u8* pDst, uint16 color0, uint16 color1); + + static color_quad_u8 unpack_endpoint(uint32 endpoints, uint32 index, bool scaled, uint32 alpha = 255U); + static uint32 pack_endpoints(uint32 lo, uint32 hi); +}; + +CRND_DEFINE_BITWISE_MOVABLE(dxt1_block); + +struct dxt3_block { + enum { cNumAlphaBytes = 8 }; + uint8 m_alpha[cNumAlphaBytes]; + + void set_alpha(uint32 x, uint32 y, uint32 value, bool scaled); + uint32 get_alpha(uint32 x, uint32 y, bool scaled) const; +}; + +CRND_DEFINE_BITWISE_MOVABLE(dxt3_block); + +struct dxt5_block { + uint8 m_endpoints[2]; + + enum { cNumSelectorBytes = 6 }; + uint8 m_selectors[cNumSelectorBytes]; + + inline void clear() { + utils::zero_this(this); + } + + inline uint32 get_low_alpha() const { + return m_endpoints[0]; + } + + inline uint32 get_high_alpha() const { + return m_endpoints[1]; + } + + inline void set_low_alpha(uint32 i) { + CRND_ASSERT(i <= cUINT8_MAX); + m_endpoints[0] = static_cast(i); + } + + inline void set_high_alpha(uint32 i) { + CRND_ASSERT(i <= cUINT8_MAX); + m_endpoints[1] = static_cast(i); + } + + uint32 get_endpoints_as_word() const { return m_endpoints[0] | (m_endpoints[1] << 8); } + + uint32 get_selectors_as_word(uint32 index) { + CRND_ASSERT(index < 3); + return m_selectors[index * 2] | (m_selectors[index * 2 + 1] << 8); + } + + inline uint32 get_selector(uint32 x, uint32 y) const { + CRND_ASSERT((x < 4U) && (y < 4U)); + + uint32 selector_index = (y * 4) + x; + uint32 bit_index = selector_index * cDXT5SelectorBits; + + uint32 byte_index = bit_index >> 3; + uint32 bit_ofs = bit_index & 7; + + uint32 v = m_selectors[byte_index]; + if (byte_index < (cNumSelectorBytes - 1)) + v |= (m_selectors[byte_index + 1] << 8); + + return (v >> bit_ofs) & 7; + } + + inline void set_selector(uint32 x, uint32 y, uint32 val) { + CRND_ASSERT((x < 4U) && (y < 4U) && (val < 8U)); + + uint32 selector_index = (y * 4) + x; + uint32 bit_index = selector_index * cDXT5SelectorBits; + + uint32 byte_index = bit_index >> 3; + uint32 bit_ofs = bit_index & 7; + + uint32 v = m_selectors[byte_index]; + if (byte_index < (cNumSelectorBytes - 1)) + v |= (m_selectors[byte_index + 1] << 8); + + v &= (~(7 << bit_ofs)); + v |= (val << bit_ofs); + + m_selectors[byte_index] = static_cast(v); + if (byte_index < (cNumSelectorBytes - 1)) + m_selectors[byte_index + 1] = static_cast(v >> 8); + } + + // Results written to alpha channel. + static uint32 get_block_values6(color_quad_u8* pDst, uint32 l, uint32 h); + static uint32 get_block_values8(color_quad_u8* pDst, uint32 l, uint32 h); + static uint32 get_block_values(color_quad_u8* pDst, uint32 l, uint32 h); + + static uint32 get_block_values6(uint32* pDst, uint32 l, uint32 h); + static uint32 get_block_values8(uint32* pDst, uint32 l, uint32 h); + // pDst must point to an array at least cDXT5SelectorValues long. + static uint32 get_block_values(uint32* pDst, uint32 l, uint32 h); + + static uint32 unpack_endpoint(uint32 packed, uint32 index); + static uint32 pack_endpoints(uint32 lo, uint32 hi); +}; + +CRND_DEFINE_BITWISE_MOVABLE(dxt5_block); + +} // namespace crnd + +// File: crnd_prefix_coding.h +#ifdef _XBOX +#define CRND_PREFIX_CODING_USE_FIXED_TABLE_SIZE 1 +#else +#define CRND_PREFIX_CODING_USE_FIXED_TABLE_SIZE 0 +#endif + +namespace crnd { +namespace prefix_coding { +const uint32 cMaxExpectedCodeSize = 16; +const uint32 cMaxSupportedSyms = 8192; +const uint32 cMaxTableBits = 11; + +class decoder_tables { + public: + inline decoder_tables() + : m_cur_lookup_size(0), m_lookup(NULL), m_cur_sorted_symbol_order_size(0), m_sorted_symbol_order(NULL) { + } + + inline decoder_tables(const decoder_tables& other) + : m_cur_lookup_size(0), m_lookup(NULL), m_cur_sorted_symbol_order_size(0), m_sorted_symbol_order(NULL) { + *this = other; + } + + decoder_tables& operator=(const decoder_tables& other) { + if (this == &other) + return *this; + + clear(); + + memcpy(this, &other, sizeof(*this)); + + if (other.m_lookup) { + m_lookup = crnd_new_array(m_cur_lookup_size); + if (m_lookup) + memcpy(m_lookup, other.m_lookup, sizeof(m_lookup[0]) * m_cur_lookup_size); + } + + if (other.m_sorted_symbol_order) { + m_sorted_symbol_order = crnd_new_array(m_cur_sorted_symbol_order_size); + if (m_sorted_symbol_order) + memcpy(m_sorted_symbol_order, other.m_sorted_symbol_order, sizeof(m_sorted_symbol_order[0]) * m_cur_sorted_symbol_order_size); + } + + return *this; + } + + inline void clear() { + if (m_lookup) { + crnd_delete_array(m_lookup); + m_lookup = 0; + m_cur_lookup_size = 0; + } + + if (m_sorted_symbol_order) { + crnd_delete_array(m_sorted_symbol_order); + m_sorted_symbol_order = NULL; + m_cur_sorted_symbol_order_size = 0; + } + } + + inline ~decoder_tables() { + if (m_lookup) + crnd_delete_array(m_lookup); + + if (m_sorted_symbol_order) + crnd_delete_array(m_sorted_symbol_order); + } + + bool init(uint32 num_syms, const uint8* pCodesizes, uint32 table_bits); + + // DO NOT use any complex classes here - it is bitwise copied. + + uint32 m_num_syms; + uint32 m_total_used_syms; + uint32 m_table_bits; + uint32 m_table_shift; + uint32 m_table_max_code; + uint32 m_decode_start_code_size; + + uint8 m_min_code_size; + uint8 m_max_code_size; + + uint32 m_max_codes[cMaxExpectedCodeSize + 1]; + int32 m_val_ptrs[cMaxExpectedCodeSize + 1]; + + uint32 m_cur_lookup_size; + uint32* m_lookup; + + uint32 m_cur_sorted_symbol_order_size; + uint16* m_sorted_symbol_order; + + inline uint32 get_unshifted_max_code(uint32 len) const { + CRND_ASSERT((len >= 1) && (len <= cMaxExpectedCodeSize)); + uint32 k = m_max_codes[len - 1]; + if (!k) + return crnd::cUINT32_MAX; + return (k - 1) >> (16 - len); + } +}; + +} // namespace prefix_coding + +} // namespace crnd + +// File: crnd_symbol_codec.h +namespace crnd { +class static_huffman_data_model { + public: + static_huffman_data_model(); + static_huffman_data_model(const static_huffman_data_model& other); + ~static_huffman_data_model(); + + static_huffman_data_model& operator=(const static_huffman_data_model& rhs); + + bool init(uint32 total_syms, const uint8* pCode_sizes, uint32 code_size_limit); + void clear(); + + inline bool is_valid() const { return m_pDecode_tables != NULL; } + + inline uint32 get_total_syms() const { return m_total_syms; } + + inline uint32 get_code_size(uint32 sym) const { return m_code_sizes[sym]; } + + inline const uint8* get_code_sizes() const { return m_code_sizes.empty() ? NULL : &m_code_sizes[0]; } + + public: + uint32 m_total_syms; + crnd::vector m_code_sizes; + prefix_coding::decoder_tables* m_pDecode_tables; + + private: + bool prepare_decoder_tables(); + uint compute_decoder_table_bits() const; + + friend class symbol_codec; +}; + +class symbol_codec { + public: + symbol_codec(); + + bool start_decoding(const uint8* pBuf, uint32 buf_size); + bool decode_receive_static_data_model(static_huffman_data_model& model); + + uint32 decode_bits(uint32 num_bits); + uint32 decode(const static_huffman_data_model& model); + + uint64 stop_decoding(); + + public: + const uint8* m_pDecode_buf; + const uint8* m_pDecode_buf_next; + const uint8* m_pDecode_buf_end; + uint32 m_decode_buf_size; + + typedef uint32 bit_buf_type; + enum { cBitBufSize = 32U }; + bit_buf_type m_bit_buf; + + int m_bit_count; + + private: + void get_bits_init(); + uint32 get_bits(uint32 num_bits); +}; + +} // namespace crnd + +namespace crnd { +void crnd_assert(const char* pExp, const char* pFile, unsigned line) { + char buf[512]; + +#if defined(WIN32) && defined(_MSC_VER) + sprintf_s(buf, sizeof(buf), "%s(%u): Assertion failure: \"%s\"\n", pFile, line, pExp); +#else + sprintf(buf, "%s(%u): Assertion failure: \"%s\"\n", pFile, line, pExp); +#endif + + crnd_output_debug_string(buf); + + puts(buf); + + if (crnd_is_debugger_present()) + crnd_debug_break(); +} + +void crnd_trace(const char* pFmt, va_list args) { + if (crnd_is_debugger_present()) { + char buf[512]; +#if defined(WIN32) && defined(_MSC_VER) + vsprintf_s(buf, sizeof(buf), pFmt, args); +#else + vsprintf(buf, pFmt, args); +#endif + + crnd_output_debug_string(buf); + } +}; + +void crnd_trace(const char* pFmt, ...) { + va_list args; + va_start(args, pFmt); + crnd_trace(pFmt, args); + va_end(args); +}; + +} // namespace crnd + +// File: checksum.cpp +// From the public domain stb.h header. +namespace crnd { +uint16 crc16(const void* pBuf, uint32 len, uint16 crc) { + crc = ~crc; + + const uint8* p = reinterpret_cast(pBuf); + while (len) { + const uint16 q = *p++ ^ (crc >> 8U); + crc <<= 8U; + + uint16 r = (q >> 4U) ^ q; + crc ^= r; + r <<= 5U; + crc ^= r; + r <<= 7U; + crc ^= r; + + len--; + } + + return static_cast(~crc); +} + +} // namespace crnd + +// File: crnd_vector.cpp +namespace crnd { +bool elemental_vector::increase_capacity(uint32 min_new_capacity, bool grow_hint, uint32 element_size, object_mover pMover) { + CRND_ASSERT(m_size <= m_capacity); + CRND_ASSERT(min_new_capacity < (0x7FFF0000U / element_size)); + + if (m_capacity >= min_new_capacity) + return true; + + uint32 new_capacity = min_new_capacity; + if ((grow_hint) && (!math::is_power_of_2(new_capacity))) + new_capacity = math::next_pow2(new_capacity); + + CRND_ASSERT(new_capacity && (new_capacity > m_capacity)); + + const uint32 desired_size = element_size * new_capacity; + size_t actual_size; + if (!pMover) { + void* new_p = crnd_realloc(m_p, desired_size, &actual_size, true); + if (!new_p) + return false; + m_p = new_p; + } else { + void* new_p = crnd_malloc(desired_size, &actual_size); + if (!new_p) + return false; + + (*pMover)(new_p, m_p, m_size); + + if (m_p) + crnd_free(m_p); + + m_p = new_p; + } + + if (actual_size > desired_size) + m_capacity = static_cast(actual_size / element_size); + else + m_capacity = new_capacity; + + return true; +} + +} // namespace crnd + +// File: crnd_utils.cpp +namespace crnd { +namespace utils { +uint32 compute_max_mips(uint32 width, uint32 height) { + if ((width | height) == 0) + return 0; + + uint32 num_mips = 1; + + while ((width > 1U) || (height > 1U)) { + width >>= 1U; + height >>= 1U; + num_mips++; + } + + return num_mips; +} + +} // namespace utils + +} // namespace crnd + +// File: crnd_prefix_coding.cpp +namespace crnd { +namespace prefix_coding { +bool decoder_tables::init(uint32 num_syms, const uint8* pCodesizes, uint32 table_bits) { + uint32 min_codes[cMaxExpectedCodeSize]; + if ((!num_syms) || (table_bits > cMaxTableBits)) + return false; + + m_num_syms = num_syms; + + uint32 num_codes[cMaxExpectedCodeSize + 1]; + utils::zero_object(num_codes); + + for (uint32 i = 0; i < num_syms; i++) { + uint32 c = pCodesizes[i]; + if (c) + num_codes[c]++; + } + + uint32 sorted_positions[cMaxExpectedCodeSize + 1]; + + uint32 cur_code = 0; + + uint32 total_used_syms = 0; + uint32 max_code_size = 0; + uint32 min_code_size = cUINT32_MAX; + for (uint32 i = 1; i <= cMaxExpectedCodeSize; i++) { + const uint32 n = num_codes[i]; + + if (!n) + m_max_codes[i - 1] = 0; //UINT_MAX; + else { + min_code_size = math::minimum(min_code_size, i); + max_code_size = math::maximum(max_code_size, i); + + min_codes[i - 1] = cur_code; + + m_max_codes[i - 1] = cur_code + n - 1; + m_max_codes[i - 1] = 1 + ((m_max_codes[i - 1] << (16 - i)) | ((1 << (16 - i)) - 1)); + + m_val_ptrs[i - 1] = total_used_syms; + + sorted_positions[i] = total_used_syms; + + cur_code += n; + total_used_syms += n; + } + + cur_code <<= 1; + } + + m_total_used_syms = total_used_syms; + + if (total_used_syms > m_cur_sorted_symbol_order_size) { + m_cur_sorted_symbol_order_size = total_used_syms; + + if (!math::is_power_of_2(total_used_syms)) + m_cur_sorted_symbol_order_size = math::minimum(num_syms, math::next_pow2(total_used_syms)); + + if (m_sorted_symbol_order) + crnd_delete_array(m_sorted_symbol_order); + + m_sorted_symbol_order = crnd_new_array(m_cur_sorted_symbol_order_size); + if (!m_sorted_symbol_order) + return false; + } + + m_min_code_size = static_cast(min_code_size); + m_max_code_size = static_cast(max_code_size); + + for (uint32 i = 0; i < num_syms; i++) { + uint32 c = pCodesizes[i]; + if (c) { + CRND_ASSERT(num_codes[c]); + + uint32 sorted_pos = sorted_positions[c]++; + + CRND_ASSERT(sorted_pos < total_used_syms); + + m_sorted_symbol_order[sorted_pos] = static_cast(i); + } + } + + if (table_bits <= m_min_code_size) + table_bits = 0; + m_table_bits = table_bits; + + if (table_bits) { + uint32 table_size = 1 << table_bits; + if (table_size > m_cur_lookup_size) { + m_cur_lookup_size = table_size; + + if (m_lookup) + crnd_delete_array(m_lookup); + + m_lookup = crnd_new_array(table_size); + if (!m_lookup) + return false; + } + + memset(m_lookup, 0xFF, (uint)sizeof(m_lookup[0]) * (1UL << table_bits)); + + for (uint32 codesize = 1; codesize <= table_bits; codesize++) { + if (!num_codes[codesize]) + continue; + + const uint32 fillsize = table_bits - codesize; + const uint32 fillnum = 1 << fillsize; + + const uint32 min_code = min_codes[codesize - 1]; + const uint32 max_code = get_unshifted_max_code(codesize); + const uint32 val_ptr = m_val_ptrs[codesize - 1]; + + for (uint32 code = min_code; code <= max_code; code++) { + const uint32 sym_index = m_sorted_symbol_order[val_ptr + code - min_code]; + CRND_ASSERT(pCodesizes[sym_index] == codesize); + + for (uint32 j = 0; j < fillnum; j++) { + const uint32 t = j + (code << fillsize); + + CRND_ASSERT(t < (1U << table_bits)); + + CRND_ASSERT(m_lookup[t] == cUINT32_MAX); + + m_lookup[t] = sym_index | (codesize << 16U); + } + } + } + } + + for (uint32 i = 0; i < cMaxExpectedCodeSize; i++) + m_val_ptrs[i] -= min_codes[i]; + + m_table_max_code = 0; + m_decode_start_code_size = m_min_code_size; + + if (table_bits) { + uint32 i; + for (i = table_bits; i >= 1; i--) { + if (num_codes[i]) { + m_table_max_code = m_max_codes[i - 1]; + break; + } + } + if (i >= 1) { + m_decode_start_code_size = table_bits + 1; + for (uint32 j = table_bits + 1; j <= max_code_size; j++) { + if (num_codes[j]) { + m_decode_start_code_size = j; + break; + } + } + } + } + + // sentinels + m_max_codes[cMaxExpectedCodeSize] = cUINT32_MAX; + m_val_ptrs[cMaxExpectedCodeSize] = 0xFFFFF; + + m_table_shift = 32 - m_table_bits; + return true; +} + +} // namespace prefix_codig + +} // namespace crnd + +// File: crnd_platform.cpp +namespace crnd { +bool crnd_is_debugger_present() { +#ifdef CRND_DEVEL + return IsDebuggerPresent() != 0; +#else + return false; +#endif +} + +void crnd_debug_break() { +#ifdef CRND_DEVEL + DebugBreak(); +#endif +} + +void crnd_output_debug_string(const char* p) { + (void)p; +#ifdef CRND_DEVEL + OutputDebugStringA(p); +#endif +} + +} // namespace crnd + +// File: crnd_mem.cpp +namespace crnd { +const uint32 MAX_POSSIBLE_BLOCK_SIZE = 0x7FFF0000U; + +static void* crnd_default_realloc(void* p, size_t size, size_t* pActual_size, bool movable, void*) { + void* p_new; + + if (!p) { + p_new = ::malloc(size); + + if (pActual_size) { +#ifdef WIN32 + *pActual_size = p_new ? ::_msize(p_new) : 0; +#elif defined(__APPLE__) + *pActual_size = p_new ? malloc_size(p_new) : 0; +#else + *pActual_size = p_new ? malloc_usable_size(p_new) : 0; +#endif + } + } else if (!size) { + ::free(p); + p_new = NULL; + + if (pActual_size) + *pActual_size = 0; + } else { + void* p_final_block = p; +#ifdef WIN32 + p_new = ::_expand(p, size); +#else + p_new = NULL; +#endif + + if (p_new) + p_final_block = p_new; + else if (movable) { + p_new = ::realloc(p, size); + + if (p_new) + p_final_block = p_new; + } + + if (pActual_size) { +#ifdef WIN32 + *pActual_size = ::_msize(p_final_block); +#elif defined(__APPLE__) + *pActual_size = ::malloc_size(p_final_block); +#else + *pActual_size = ::malloc_usable_size(p_final_block); +#endif + } + } + + return p_new; +} + +static size_t crnd_default_msize(void* p, void* pUser_data) { + (void)pUser_data; +#ifdef WIN32 + return p ? _msize(p) : 0; +#elif defined(__APPLE__) + return p ? malloc_size(p) : 0; +#else + return p ? malloc_usable_size(p) : 0; +#endif +} + +static crnd_realloc_func g_pRealloc = crnd_default_realloc; +static crnd_msize_func g_pMSize = crnd_default_msize; +static void* g_pUser_data; + +void crnd_set_memory_callbacks(crnd_realloc_func pRealloc, crnd_msize_func pMSize, void* pUser_data) { + if ((!pRealloc) || (!pMSize)) { + g_pRealloc = crnd_default_realloc; + g_pMSize = crnd_default_msize; + g_pUser_data = NULL; + } else { + g_pRealloc = pRealloc; + g_pMSize = pMSize; + g_pUser_data = pUser_data; + } +} + +static inline void crnd_mem_error(const char* p_msg) { + crnd_assert(p_msg, __FILE__, __LINE__); +} + +void* crnd_malloc(size_t size, size_t* pActual_size) { + size = (size + sizeof(uint32) - 1U) & ~(sizeof(uint32) - 1U); + if (!size) + size = sizeof(uint32); + + if (size > MAX_POSSIBLE_BLOCK_SIZE) { + crnd_mem_error("crnd_malloc: size too big"); + return NULL; + } + + size_t actual_size = size; + uint8* p_new = static_cast((*g_pRealloc)(NULL, size, &actual_size, true, g_pUser_data)); + + if (pActual_size) + *pActual_size = actual_size; + + if ((!p_new) || (actual_size < size)) { + crnd_mem_error("crnd_malloc: out of memory"); + return NULL; + } + + CRND_ASSERT(((uint32) reinterpret_cast(p_new) & (CRND_MIN_ALLOC_ALIGNMENT - 1)) == 0); + + return p_new; +} + +void* crnd_realloc(void* p, size_t size, size_t* pActual_size, bool movable) { + if ((uint32) reinterpret_cast(p) & (CRND_MIN_ALLOC_ALIGNMENT - 1)) { + crnd_mem_error("crnd_realloc: bad ptr"); + return NULL; + } + + if (size > MAX_POSSIBLE_BLOCK_SIZE) { + crnd_mem_error("crnd_malloc: size too big"); + return NULL; + } + + size_t actual_size = size; + void* p_new = (*g_pRealloc)(p, size, &actual_size, movable, g_pUser_data); + + if (pActual_size) + *pActual_size = actual_size; + + CRND_ASSERT(((uint32) reinterpret_cast(p_new) & (CRND_MIN_ALLOC_ALIGNMENT - 1)) == 0); + + return p_new; +} + +void crnd_free(void* p) { + if (!p) + return; + + if ((uint32) reinterpret_cast(p) & (CRND_MIN_ALLOC_ALIGNMENT - 1)) { + crnd_mem_error("crnd_free: bad ptr"); + return; + } + + (*g_pRealloc)(p, 0, NULL, true, g_pUser_data); +} + +size_t crnd_msize(void* p) { + if (!p) + return 0; + + if ((uint32) reinterpret_cast(p) & (CRND_MIN_ALLOC_ALIGNMENT - 1)) { + crnd_mem_error("crnd_msize: bad ptr"); + return 0; + } + + return (*g_pMSize)(p, g_pUser_data); +} + +} // namespace crnd + +// File: crnd_math.cpp +namespace crnd { +namespace math { +uint32 g_bitmasks[32] = + { + 1U << 0U, 1U << 1U, 1U << 2U, 1U << 3U, + 1U << 4U, 1U << 5U, 1U << 6U, 1U << 7U, + 1U << 8U, 1U << 9U, 1U << 10U, 1U << 11U, + 1U << 12U, 1U << 13U, 1U << 14U, 1U << 15U, + 1U << 16U, 1U << 17U, 1U << 18U, 1U << 19U, + 1U << 20U, 1U << 21U, 1U << 22U, 1U << 23U, + 1U << 24U, 1U << 25U, 1U << 26U, 1U << 27U, + 1U << 28U, 1U << 29U, 1U << 30U, 1U << 31U}; + +} // namespace math +} // namespace crnd + +// File: crnd_info.cpp +namespace crnd { +#define CRND_FOURCC(a, b, c, d) ((a) | ((b) << 8U) | ((c) << 16U) | ((d) << 24U)) + +uint32 crnd_crn_format_to_fourcc(crn_format fmt) { + switch (fmt) { + case cCRNFmtDXT1: + return CRND_FOURCC('D', 'X', 'T', '1'); + case cCRNFmtDXT3: + return CRND_FOURCC('D', 'X', 'T', '3'); + case cCRNFmtDXT5: + return CRND_FOURCC('D', 'X', 'T', '5'); + case cCRNFmtDXN_XY: + return CRND_FOURCC('A', '2', 'X', 'Y'); + case cCRNFmtDXN_YX: + return CRND_FOURCC('A', 'T', 'I', '2'); + case cCRNFmtDXT5A: + return CRND_FOURCC('A', 'T', 'I', '1'); + case cCRNFmtDXT5_CCxY: + return CRND_FOURCC('C', 'C', 'x', 'Y'); + case cCRNFmtDXT5_xGxR: + return CRND_FOURCC('x', 'G', 'x', 'R'); + case cCRNFmtDXT5_xGBR: + return CRND_FOURCC('x', 'G', 'B', 'R'); + case cCRNFmtDXT5_AGBR: + return CRND_FOURCC('A', 'G', 'B', 'R'); + case cCRNFmtETC1: + return CRND_FOURCC('E', 'T', 'C', '1'); + case cCRNFmtETC2: + return CRND_FOURCC('E', 'T', 'C', '2'); + case cCRNFmtETC2A: + return CRND_FOURCC('E', 'T', '2', 'A'); + default: + break; + } + CRND_ASSERT(false); + return 0; +} + +crn_format crnd_get_fundamental_dxt_format(crn_format fmt) { + switch (fmt) { + case cCRNFmtDXT5_CCxY: + case cCRNFmtDXT5_xGxR: + case cCRNFmtDXT5_xGBR: + case cCRNFmtDXT5_AGBR: + return cCRNFmtDXT5; + default: + break; + } + return fmt; +} + +uint32 crnd_get_crn_format_bits_per_texel(crn_format fmt) { + switch (fmt) { + case cCRNFmtDXT1: + case cCRNFmtDXT5A: + case cCRNFmtETC1: + case cCRNFmtETC2: + return 4; + case cCRNFmtDXT3: + case cCRNFmtDXT5: + case cCRNFmtDXN_XY: + case cCRNFmtDXN_YX: + case cCRNFmtDXT5_CCxY: + case cCRNFmtDXT5_xGxR: + case cCRNFmtDXT5_xGBR: + case cCRNFmtDXT5_AGBR: + case cCRNFmtETC2A: + return 8; + default: + break; + } + CRND_ASSERT(false); + return 0; +} + +uint32 crnd_get_bytes_per_dxt_block(crn_format fmt) { + return (crnd_get_crn_format_bits_per_texel(fmt) << 4) >> 3; +} + +// TODO: tmp_header isn't used/This function is a helper to support old headers. +const crn_header* crnd_get_header(const void* pData, uint32 data_size) { + if ((!pData) || (data_size < sizeof(crn_header))) + return NULL; + + const crn_header& file_header = *static_cast(pData); + if (file_header.m_sig != crn_header::cCRNSigValue) + return NULL; + + if ((file_header.m_header_size < sizeof(crn_header)) || (data_size < file_header.m_data_size)) + return NULL; + + return &file_header; +} + +bool crnd_validate_file(const void* pData, uint32 data_size, crn_file_info* pFile_info) { + if (pFile_info) { + if (pFile_info->m_struct_size != sizeof(crn_file_info)) + return false; + + memset(&pFile_info->m_struct_size + 1, 0, sizeof(crn_file_info) - sizeof(pFile_info->m_struct_size)); + } + + if ((!pData) || (data_size < cCRNHeaderMinSize)) + return false; + + const crn_header* pHeader = crnd_get_header(pData, data_size); + if (!pHeader) + return false; + + const uint32 header_crc = crc16(&pHeader->m_data_size, (uint32)(pHeader->m_header_size - ((const uint8*)&pHeader->m_data_size - (const uint8*)pHeader))); + if (header_crc != pHeader->m_header_crc16) + return false; + + const uint32 data_crc = crc16((const uint8*)pData + pHeader->m_header_size, pHeader->m_data_size - pHeader->m_header_size); + if (data_crc != pHeader->m_data_crc16) + return false; + + if ((pHeader->m_faces != 1) && (pHeader->m_faces != 6)) + return false; + if ((pHeader->m_width < 1) || (pHeader->m_width > cCRNMaxLevelResolution)) + return false; + if ((pHeader->m_height < 1) || (pHeader->m_height > cCRNMaxLevelResolution)) + return false; + if ((pHeader->m_levels < 1) || (pHeader->m_levels > utils::compute_max_mips(pHeader->m_width, pHeader->m_height))) + return false; + if (((int)pHeader->m_format < cCRNFmtDXT1) || ((int)pHeader->m_format >= cCRNFmtTotal)) + return false; + + if (pFile_info) { + pFile_info->m_actual_data_size = pHeader->m_data_size; + pFile_info->m_header_size = pHeader->m_header_size; + pFile_info->m_total_palette_size = pHeader->m_color_endpoints.m_size + pHeader->m_color_selectors.m_size + pHeader->m_alpha_endpoints.m_size + pHeader->m_alpha_selectors.m_size; + pFile_info->m_tables_size = pHeader->m_tables_size; + + pFile_info->m_levels = pHeader->m_levels; + + for (uint32 i = 0; i < pHeader->m_levels; i++) { + uint32 next_ofs = pHeader->m_data_size; + + // assumes the levels are packed together sequentially + if ((i + 1) < pHeader->m_levels) + next_ofs = pHeader->m_level_ofs[i + 1]; + + pFile_info->m_level_compressed_size[i] = next_ofs - pHeader->m_level_ofs[i]; + } + + pFile_info->m_color_endpoint_palette_entries = pHeader->m_color_endpoints.m_num; + pFile_info->m_color_selector_palette_entries = pHeader->m_color_selectors.m_num; + ; + pFile_info->m_alpha_endpoint_palette_entries = pHeader->m_alpha_endpoints.m_num; + ; + pFile_info->m_alpha_selector_palette_entries = pHeader->m_alpha_selectors.m_num; + ; + } + + return true; +} + +bool crnd_get_texture_info(const void* pData, uint32 data_size, crn_texture_info* pInfo) { + if ((!pData) || (data_size < sizeof(crn_header)) || (!pInfo)) + return false; + + if (pInfo->m_struct_size != sizeof(crn_texture_info)) + return false; + + const crn_header* pHeader = crnd_get_header(pData, data_size); + if (!pHeader) + return false; + + pInfo->m_width = pHeader->m_width; + pInfo->m_height = pHeader->m_height; + pInfo->m_levels = pHeader->m_levels; + pInfo->m_faces = pHeader->m_faces; + pInfo->m_format = static_cast((uint32)pHeader->m_format); + pInfo->m_bytes_per_block = pHeader->m_format == cCRNFmtDXT1 || pHeader->m_format == cCRNFmtDXT5A || pHeader->m_format == cCRNFmtETC1 || pHeader->m_format == cCRNFmtETC2 ? 8 : 16; + pInfo->m_userdata0 = pHeader->m_userdata0; + pInfo->m_userdata1 = pHeader->m_userdata1; + + return true; +} + +bool crnd_get_level_info(const void* pData, uint32 data_size, uint32 level_index, crn_level_info* pLevel_info) { + if ((!pData) || (data_size < cCRNHeaderMinSize) || (!pLevel_info)) + return false; + + if (pLevel_info->m_struct_size != sizeof(crn_level_info)) + return false; + + const crn_header* pHeader = crnd_get_header(pData, data_size); + if (!pHeader) + return false; + + if (level_index >= pHeader->m_levels) + return false; + + uint32 width = math::maximum(1U, pHeader->m_width >> level_index); + uint32 height = math::maximum(1U, pHeader->m_height >> level_index); + + pLevel_info->m_width = width; + pLevel_info->m_height = height; + pLevel_info->m_faces = pHeader->m_faces; + pLevel_info->m_blocks_x = (width + 3) >> 2; + pLevel_info->m_blocks_y = (height + 3) >> 2; + pLevel_info->m_bytes_per_block = ((pHeader->m_format == cCRNFmtDXT1) || (pHeader->m_format == cCRNFmtDXT5A)) ? 8 : 16; + pLevel_info->m_format = static_cast((uint32)pHeader->m_format); + + return true; +} + +const void* crnd_get_level_data(const void* pData, uint32 data_size, uint32 level_index, uint32* pSize) { + if (pSize) + *pSize = 0; + + if ((!pData) || (data_size < cCRNHeaderMinSize)) + return NULL; + + const crn_header* pHeader = crnd_get_header(pData, data_size); + if (!pHeader) + return NULL; + + if (level_index >= pHeader->m_levels) + return NULL; + + uint32 cur_level_ofs = pHeader->m_level_ofs[level_index]; + + if (pSize) { + uint32 next_level_ofs = data_size; + if ((level_index + 1) < (pHeader->m_levels)) + next_level_ofs = pHeader->m_level_ofs[level_index + 1]; + + *pSize = next_level_ofs - cur_level_ofs; + } + + return static_cast(pData) + cur_level_ofs; +} + +uint32 crnd_get_segmented_file_size(const void* pData, uint32 data_size) { + if ((!pData) || (data_size < cCRNHeaderMinSize)) + return false; + + const crn_header* pHeader = crnd_get_header(pData, data_size); + if (!pHeader) + return false; + + uint32 size = pHeader->m_header_size; + + size = math::maximum(size, pHeader->m_color_endpoints.m_ofs + pHeader->m_color_endpoints.m_size); + size = math::maximum(size, pHeader->m_color_selectors.m_ofs + pHeader->m_color_selectors.m_size); + size = math::maximum(size, pHeader->m_alpha_endpoints.m_ofs + pHeader->m_alpha_endpoints.m_size); + size = math::maximum(size, pHeader->m_alpha_selectors.m_ofs + pHeader->m_alpha_selectors.m_size); + size = math::maximum(size, pHeader->m_tables_ofs + pHeader->m_tables_size); + + return size; +} + +bool crnd_create_segmented_file(const void* pData, uint32 data_size, void* pBase_data, uint base_data_size) { + if ((!pData) || (data_size < cCRNHeaderMinSize)) + return false; + + const crn_header* pHeader = crnd_get_header(pData, data_size); + if (!pHeader) + return false; + + if (pHeader->m_flags & cCRNHeaderFlagSegmented) + return false; + + const uint actual_base_data_size = crnd_get_segmented_file_size(pData, data_size); + if (base_data_size < actual_base_data_size) + return false; + + memcpy(pBase_data, pData, actual_base_data_size); + + crn_header& new_header = *static_cast(pBase_data); + new_header.m_flags = new_header.m_flags | cCRNHeaderFlagSegmented; + new_header.m_data_size = actual_base_data_size; + + new_header.m_data_crc16 = crc16((const uint8*)pBase_data + new_header.m_header_size, new_header.m_data_size - new_header.m_header_size); + + new_header.m_header_crc16 = crc16(&new_header.m_data_size, new_header.m_header_size - (uint32)((const uint8*)&new_header.m_data_size - (const uint8*)&new_header)); + + CRND_ASSERT(crnd_validate_file(&new_header, actual_base_data_size, NULL)); + + return true; +} + +} // namespace crnd + +// File: symbol_codec.cpp +namespace crnd { +static_huffman_data_model::static_huffman_data_model() + : m_total_syms(0), + m_pDecode_tables(NULL) { +} + +static_huffman_data_model::static_huffman_data_model(const static_huffman_data_model& other) + : m_total_syms(0), + m_pDecode_tables(NULL) { + *this = other; +} + +static_huffman_data_model::~static_huffman_data_model() { + if (m_pDecode_tables) + crnd_delete(m_pDecode_tables); +} + +static_huffman_data_model& static_huffman_data_model::operator=(const static_huffman_data_model& rhs) { + if (this == &rhs) + return *this; + + m_total_syms = rhs.m_total_syms; + m_code_sizes = rhs.m_code_sizes; + if (m_code_sizes.get_alloc_failed()) { + clear(); + return *this; + } + + if (rhs.m_pDecode_tables) { + if (m_pDecode_tables) + *m_pDecode_tables = *rhs.m_pDecode_tables; + else + m_pDecode_tables = crnd_new(*rhs.m_pDecode_tables); + } else { + crnd_delete(m_pDecode_tables); + m_pDecode_tables = NULL; + } + + return *this; +} + +void static_huffman_data_model::clear() { + m_total_syms = 0; + m_code_sizes.clear(); + if (m_pDecode_tables) { + crnd_delete(m_pDecode_tables); + m_pDecode_tables = NULL; + } +} + +bool static_huffman_data_model::init(uint32 total_syms, const uint8* pCode_sizes, uint32 code_size_limit) { + CRND_ASSERT((total_syms >= 1) && (total_syms <= prefix_coding::cMaxSupportedSyms) && (code_size_limit >= 1)); + + code_size_limit = math::minimum(code_size_limit, prefix_coding::cMaxExpectedCodeSize); + + if (!m_code_sizes.resize(total_syms)) + return false; + + uint32 min_code_size = cUINT32_MAX; + uint32 max_code_size = 0; + + for (uint32 i = 0; i < total_syms; i++) { + uint32 s = pCode_sizes[i]; + m_code_sizes[i] = static_cast(s); + min_code_size = math::minimum(min_code_size, s); + max_code_size = math::maximum(max_code_size, s); + } + + if ((max_code_size < 1) || (max_code_size > 32) || (min_code_size > code_size_limit)) + return false; + + if (max_code_size > code_size_limit) + return false; + + if (!m_pDecode_tables) + m_pDecode_tables = crnd_new(); + + if (!m_pDecode_tables->init(m_total_syms, &m_code_sizes[0], compute_decoder_table_bits())) + return false; + + return true; +} + +bool static_huffman_data_model::prepare_decoder_tables() { + uint32 total_syms = m_code_sizes.size(); + + CRND_ASSERT((total_syms >= 1) && (total_syms <= prefix_coding::cMaxSupportedSyms)); + + m_total_syms = total_syms; + + if (!m_pDecode_tables) + m_pDecode_tables = crnd_new(); + + return m_pDecode_tables->init(m_total_syms, &m_code_sizes[0], compute_decoder_table_bits()); +} + +uint static_huffman_data_model::compute_decoder_table_bits() const { +#if CRND_PREFIX_CODING_USE_FIXED_TABLE_SIZE + return prefix_coding::cMaxTableBits; +#else + uint32 decoder_table_bits = 0; + if (m_total_syms > 16) + decoder_table_bits = static_cast(math::minimum(1 + math::ceil_log2i(m_total_syms), prefix_coding::cMaxTableBits)); + return decoder_table_bits; +#endif +} + +symbol_codec::symbol_codec() + : m_pDecode_buf(NULL), + m_pDecode_buf_next(NULL), + m_pDecode_buf_end(NULL), + m_decode_buf_size(0), + m_bit_buf(0), + m_bit_count(0) { +} + +// Code length encoding symbols: +// 0-16 - actual code lengths +const uint32 cMaxCodelengthCodes = 21; + +const uint32 cSmallZeroRunCode = 17; +const uint32 cLargeZeroRunCode = 18; +const uint32 cSmallRepeatCode = 19; +const uint32 cLargeRepeatCode = 20; + +const uint32 cMinSmallZeroRunSize = 3; +const uint32 cMaxSmallZeroRunSize = 10; +const uint32 cMinLargeZeroRunSize = 11; +const uint32 cMaxLargeZeroRunSize = 138; + +const uint32 cSmallMinNonZeroRunSize = 3; +const uint32 cSmallMaxNonZeroRunSize = 6; +const uint32 cLargeMinNonZeroRunSize = 7; +const uint32 cLargeMaxNonZeroRunSize = 70; + +const uint32 cSmallZeroRunExtraBits = 3; +const uint32 cLargeZeroRunExtraBits = 7; +const uint32 cSmallNonZeroRunExtraBits = 2; +const uint32 cLargeNonZeroRunExtraBits = 6; + +static const uint8 g_most_probable_codelength_codes[] = + { + cSmallZeroRunCode, cLargeZeroRunCode, + cSmallRepeatCode, cLargeRepeatCode, + + 0, 8, + 7, 9, + 6, 10, + 5, 11, + 4, 12, + 3, 13, + 2, 14, + 1, 15, + 16}; +const uint32 cNumMostProbableCodelengthCodes = sizeof(g_most_probable_codelength_codes) / sizeof(g_most_probable_codelength_codes[0]); + +bool symbol_codec::decode_receive_static_data_model(static_huffman_data_model& model) { + const uint32 total_used_syms = decode_bits(math::total_bits(prefix_coding::cMaxSupportedSyms)); + + if (!total_used_syms) { + model.clear(); + return true; + } + + if (!model.m_code_sizes.resize(total_used_syms)) + return false; + + memset(&model.m_code_sizes[0], 0, sizeof(model.m_code_sizes[0]) * total_used_syms); + + const uint32 num_codelength_codes_to_send = decode_bits(5); + if ((num_codelength_codes_to_send < 1) || (num_codelength_codes_to_send > cMaxCodelengthCodes)) + return false; + + static_huffman_data_model dm; + if (!dm.m_code_sizes.resize(cMaxCodelengthCodes)) + return false; + + for (uint32 i = 0; i < num_codelength_codes_to_send; i++) + dm.m_code_sizes[g_most_probable_codelength_codes[i]] = static_cast(decode_bits(3)); + + if (!dm.prepare_decoder_tables()) + return false; + + uint32 ofs = 0; + while (ofs < total_used_syms) { + const uint32 num_remaining = total_used_syms - ofs; + + uint32 code = decode(dm); + if (code <= 16) + model.m_code_sizes[ofs++] = static_cast(code); + else if (code == cSmallZeroRunCode) { + uint32 len = decode_bits(cSmallZeroRunExtraBits) + cMinSmallZeroRunSize; + if (len > num_remaining) + return false; + ofs += len; + } else if (code == cLargeZeroRunCode) { + uint32 len = decode_bits(cLargeZeroRunExtraBits) + cMinLargeZeroRunSize; + if (len > num_remaining) + return false; + ofs += len; + } else if ((code == cSmallRepeatCode) || (code == cLargeRepeatCode)) { + uint32 len; + if (code == cSmallRepeatCode) + len = decode_bits(cSmallNonZeroRunExtraBits) + cSmallMinNonZeroRunSize; + else + len = decode_bits(cLargeNonZeroRunExtraBits) + cLargeMinNonZeroRunSize; + + if ((!ofs) || (len > num_remaining)) + return false; + const uint32 prev = model.m_code_sizes[ofs - 1]; + if (!prev) + return false; + const uint32 end = ofs + len; + while (ofs < end) + model.m_code_sizes[ofs++] = static_cast(prev); + } else { + CRND_ASSERT(0); + return false; + } + } + + if (ofs != total_used_syms) + return false; + + return model.prepare_decoder_tables(); +} + +bool symbol_codec::start_decoding(const uint8* pBuf, uint32 buf_size) { + if (!buf_size) + return false; + + m_pDecode_buf = pBuf; + m_pDecode_buf_next = pBuf; + m_decode_buf_size = buf_size; + m_pDecode_buf_end = pBuf + buf_size; + + get_bits_init(); + + return true; +} + +void symbol_codec::get_bits_init() { + m_bit_buf = 0; + m_bit_count = 0; +} + +uint32 symbol_codec::decode_bits(uint32 num_bits) { + if (!num_bits) + return 0; + + if (num_bits > 16) { + uint32 a = get_bits(num_bits - 16); + uint32 b = get_bits(16); + + return (a << 16) | b; + } else + return get_bits(num_bits); +} + +uint32 symbol_codec::get_bits(uint32 num_bits) { + CRND_ASSERT(num_bits <= 32U); + + while (m_bit_count < (int)num_bits) { + bit_buf_type c = 0; + if (m_pDecode_buf_next != m_pDecode_buf_end) + c = *m_pDecode_buf_next++; + + m_bit_count += 8; + CRND_ASSERT(m_bit_count <= cBitBufSize); + + m_bit_buf |= (c << (cBitBufSize - m_bit_count)); + } + + uint32 result = static_cast(m_bit_buf >> (cBitBufSize - num_bits)); + + m_bit_buf <<= num_bits; + m_bit_count -= num_bits; + + return result; +} + +uint32 symbol_codec::decode(const static_huffman_data_model& model) { + const prefix_coding::decoder_tables* pTables = model.m_pDecode_tables; + + if (m_bit_count < 24) { + if (m_bit_count < 16) { + uint32 c0 = 0, c1 = 0; + const uint8* p = m_pDecode_buf_next; + if (p < m_pDecode_buf_end) + c0 = *p++; + if (p < m_pDecode_buf_end) + c1 = *p++; + m_pDecode_buf_next = p; + m_bit_count += 16; + uint32 c = (c0 << 8) | c1; + m_bit_buf |= (c << (32 - m_bit_count)); + } else { + uint32 c = (m_pDecode_buf_next < m_pDecode_buf_end) ? *m_pDecode_buf_next++ : 0; + m_bit_count += 8; + m_bit_buf |= (c << (32 - m_bit_count)); + } + } + + uint32 k = (m_bit_buf >> 16) + 1; + uint32 sym, len; + + if (k <= pTables->m_table_max_code) { + uint32 t = pTables->m_lookup[m_bit_buf >> (32 - pTables->m_table_bits)]; + + CRND_ASSERT(t != cUINT32_MAX); + sym = t & cUINT16_MAX; + len = t >> 16; + + CRND_ASSERT(model.m_code_sizes[sym] == len); + } else { + len = pTables->m_decode_start_code_size; + + for (;;) { + if (k <= pTables->m_max_codes[len - 1]) + break; + len++; + } + + int val_ptr = pTables->m_val_ptrs[len - 1] + (m_bit_buf >> (32 - len)); + + if (((uint32)val_ptr >= model.m_total_syms)) { + // corrupted stream, or a bug + CRND_ASSERT(0); + return 0; + } + + sym = pTables->m_sorted_symbol_order[val_ptr]; + } + + m_bit_buf <<= len; + m_bit_count -= len; + + return sym; +} + +uint64 symbol_codec::stop_decoding() { + return static_cast(m_pDecode_buf_next - m_pDecode_buf); +} + +} // namespace crnd + +// File: crnd_dxt.cpp +namespace crnd { +const uint8 g_dxt1_to_linear[cDXT1SelectorValues] = {0U, 3U, 1U, 2U}; +const uint8 g_dxt1_from_linear[cDXT1SelectorValues] = {0U, 2U, 3U, 1U}; +const uint8 g_etc1_from_linear[cDXT1SelectorValues] = {3U, 2U, 0U, 1U}; + +const uint8 g_dxt5_to_linear[cDXT5SelectorValues] = {0U, 7U, 1U, 2U, 3U, 4U, 5U, 6U}; +const uint8 g_dxt5_from_linear[cDXT5SelectorValues] = {0U, 2U, 3U, 4U, 5U, 6U, 7U, 1U}; + +const uint8 g_six_alpha_invert_table[cDXT5SelectorValues] = {1, 0, 5, 4, 3, 2, 6, 7}; +const uint8 g_eight_alpha_invert_table[cDXT5SelectorValues] = {1, 0, 7, 6, 5, 4, 3, 2}; + +uint16 dxt1_block::pack_color(const color_quad_u8& color, bool scaled, uint32 bias) { + uint32 r = color.r; + uint32 g = color.g; + uint32 b = color.b; + + if (scaled) { + r = (r * 31U + bias) / 255U; + g = (g * 63U + bias) / 255U; + b = (b * 31U + bias) / 255U; + } + + r = math::minimum(r, 31U); + g = math::minimum(g, 63U); + b = math::minimum(b, 31U); + + return static_cast(b | (g << 5U) | (r << 11U)); +} + +uint16 dxt1_block::pack_color(uint32 r, uint32 g, uint32 b, bool scaled, uint32 bias) { + return pack_color(color_quad_u8(r, g, b, 0), scaled, bias); +} + +color_quad_u8 dxt1_block::unpack_color(uint16 packed_color, bool scaled, uint32 alpha) { + uint32 b = packed_color & 31U; + uint32 g = (packed_color >> 5U) & 63U; + uint32 r = (packed_color >> 11U) & 31U; + + if (scaled) { + b = (b << 3U) | (b >> 2U); + g = (g << 2U) | (g >> 4U); + r = (r << 3U) | (r >> 2U); + } + + return color_quad_u8(r, g, b, alpha); +} + +void dxt1_block::unpack_color(uint32& r, uint32& g, uint32& b, uint16 packed_color, bool scaled) { + color_quad_u8 c(unpack_color(packed_color, scaled, 0)); + r = c.r; + g = c.g; + b = c.b; +} + +uint32 dxt1_block::get_block_colors3(color_quad_u8* pDst, uint16 color0, uint16 color1) { + color_quad_u8 c0(unpack_color(color0, true)); + color_quad_u8 c1(unpack_color(color1, true)); + + pDst[0] = c0; + pDst[1] = c1; + pDst[2].set((c0.r + c1.r) >> 1U, (c0.g + c1.g) >> 1U, (c0.b + c1.b) >> 1U, 255U); + pDst[3].set(0, 0, 0, 0); + + return 3; +} + +uint32 dxt1_block::get_block_colors4(color_quad_u8* pDst, uint16 color0, uint16 color1) { + color_quad_u8 c0(unpack_color(color0, true)); + color_quad_u8 c1(unpack_color(color1, true)); + + pDst[0] = c0; + pDst[1] = c1; + + // 12/14/09 - Supposed to round according to DX docs, but this conflicts with the OpenGL S3TC spec. ? + // Turns out some GPU's round and some don't. Great. + //pDst[2].set( (c0.r * 2 + c1.r + 1) / 3, (c0.g * 2 + c1.g + 1) / 3, (c0.b * 2 + c1.b + 1) / 3, 255U); + //pDst[3].set( (c1.r * 2 + c0.r + 1) / 3, (c1.g * 2 + c0.g + 1) / 3, (c1.b * 2 + c0.b + 1) / 3, 255U); + + pDst[2].set((c0.r * 2 + c1.r) / 3, (c0.g * 2 + c1.g) / 3, (c0.b * 2 + c1.b) / 3, 255U); + pDst[3].set((c1.r * 2 + c0.r) / 3, (c1.g * 2 + c0.g) / 3, (c1.b * 2 + c0.b) / 3, 255U); + + return 4; +} + +uint32 dxt1_block::get_block_colors(color_quad_u8* pDst, uint16 color0, uint16 color1) { + if (color0 > color1) + return get_block_colors4(pDst, color0, color1); + else + return get_block_colors3(pDst, color0, color1); +} + +color_quad_u8 dxt1_block::unpack_endpoint(uint32 endpoints, uint32 index, bool scaled, uint32 alpha) { + CRND_ASSERT(index < 2); + return unpack_color(static_cast((endpoints >> (index * 16U)) & 0xFFFFU), scaled, alpha); +} + +uint32 dxt1_block::pack_endpoints(uint32 lo, uint32 hi) { + CRND_ASSERT((lo <= 0xFFFFU) && (hi <= 0xFFFFU)); + return lo | (hi << 16U); +} + +void dxt3_block::set_alpha(uint32 x, uint32 y, uint32 value, bool scaled) { + CRND_ASSERT((x < cDXTBlockSize) && (y < cDXTBlockSize)); + + if (scaled) { + CRND_ASSERT(value <= 0xFF); + value = (value * 15U + 128U) / 255U; + } else { + CRND_ASSERT(value <= 0xF); + } + + uint32 ofs = (y << 1U) + (x >> 1U); + uint32 c = m_alpha[ofs]; + + c &= ~(0xF << ((x & 1U) << 2U)); + c |= (value << ((x & 1U) << 2U)); + + m_alpha[ofs] = static_cast(c); +} + +uint32 dxt3_block::get_alpha(uint32 x, uint32 y, bool scaled) const { + CRND_ASSERT((x < cDXTBlockSize) && (y < cDXTBlockSize)); + + uint32 value = m_alpha[(y << 1U) + (x >> 1U)]; + if (x & 1) + value >>= 4; + value &= 0xF; + + if (scaled) + value = (value << 4U) | value; + + return value; +} + +uint32 dxt5_block::get_block_values6(color_quad_u8* pDst, uint32 l, uint32 h) { + pDst[0].a = static_cast(l); + pDst[1].a = static_cast(h); + pDst[2].a = static_cast((l * 4 + h) / 5); + pDst[3].a = static_cast((l * 3 + h * 2) / 5); + pDst[4].a = static_cast((l * 2 + h * 3) / 5); + pDst[5].a = static_cast((l + h * 4) / 5); + pDst[6].a = 0; + pDst[7].a = 255; + return 6; +} + +uint32 dxt5_block::get_block_values8(color_quad_u8* pDst, uint32 l, uint32 h) { + pDst[0].a = static_cast(l); + pDst[1].a = static_cast(h); + pDst[2].a = static_cast((l * 6 + h) / 7); + pDst[3].a = static_cast((l * 5 + h * 2) / 7); + pDst[4].a = static_cast((l * 4 + h * 3) / 7); + pDst[5].a = static_cast((l * 3 + h * 4) / 7); + pDst[6].a = static_cast((l * 2 + h * 5) / 7); + pDst[7].a = static_cast((l + h * 6) / 7); + return 8; +} + +uint32 dxt5_block::get_block_values(color_quad_u8* pDst, uint32 l, uint32 h) { + if (l > h) + return get_block_values8(pDst, l, h); + else + return get_block_values6(pDst, l, h); +} + +uint32 dxt5_block::get_block_values6(uint32* pDst, uint32 l, uint32 h) { + pDst[0] = l; + pDst[1] = h; + pDst[2] = (l * 4 + h) / 5; + pDst[3] = (l * 3 + h * 2) / 5; + pDst[4] = (l * 2 + h * 3) / 5; + pDst[5] = (l + h * 4) / 5; + pDst[6] = 0; + pDst[7] = 255; + return 6; +} + +uint32 dxt5_block::get_block_values8(uint32* pDst, uint32 l, uint32 h) { + pDst[0] = l; + pDst[1] = h; + pDst[2] = (l * 6 + h) / 7; + pDst[3] = (l * 5 + h * 2) / 7; + pDst[4] = (l * 4 + h * 3) / 7; + pDst[5] = (l * 3 + h * 4) / 7; + pDst[6] = (l * 2 + h * 5) / 7; + pDst[7] = (l + h * 6) / 7; + return 8; +} + +uint32 dxt5_block::unpack_endpoint(uint32 packed, uint32 index) { + CRND_ASSERT(index < 2); + return (packed >> (8 * index)) & 0xFF; +} + +uint32 dxt5_block::pack_endpoints(uint32 lo, uint32 hi) { + CRND_ASSERT((lo <= 0xFF) && (hi <= 0xFF)); + return lo | (hi << 8U); +} + +uint32 dxt5_block::get_block_values(uint32* pDst, uint32 l, uint32 h) { + if (l > h) + return get_block_values8(pDst, l, h); + else + return get_block_values6(pDst, l, h); +} + +} // namespace crnd + +// File: crnd_decode.cpp + +namespace crnd { + +class crn_unpacker { + public: + inline crn_unpacker() + : m_magic(cMagicValue), + m_pData(NULL), + m_data_size(0), + m_pHeader(NULL) { + } + + inline ~crn_unpacker() { + m_magic = 0; + } + + inline bool is_valid() const { return m_magic == cMagicValue; } + + bool init(const void* pData, uint32 data_size) { + m_pHeader = crnd_get_header(pData, data_size); + if (!m_pHeader) + return false; + + m_pData = static_cast(pData); + m_data_size = data_size; + + if (!init_tables()) + return false; + + if (!decode_palettes()) + return false; + + return true; + } + + bool unpack_level( + void** pDst, uint32 dst_size_in_bytes, uint32 row_pitch_in_bytes, + uint32 level_index) { + uint32 cur_level_ofs = m_pHeader->m_level_ofs[level_index]; + + uint32 next_level_ofs = m_data_size; + if ((level_index + 1) < (m_pHeader->m_levels)) + next_level_ofs = m_pHeader->m_level_ofs[level_index + 1]; + + CRND_ASSERT(next_level_ofs > cur_level_ofs); + + return unpack_level(m_pData + cur_level_ofs, next_level_ofs - cur_level_ofs, pDst, dst_size_in_bytes, row_pitch_in_bytes, level_index); + } + + bool unpack_level( + const void* pSrc, uint32 src_size_in_bytes, + void** pDst, uint32 dst_size_in_bytes, uint32 row_pitch_in_bytes, + uint32 level_index) { + +#ifdef CRND_BUILD_DEBUG + for (uint32 f = 0; f < m_pHeader->m_faces; f++) + if (!pDst[f]) + return false; +#endif + + const uint32 width = math::maximum(m_pHeader->m_width >> level_index, 1U); + const uint32 height = math::maximum(m_pHeader->m_height >> level_index, 1U); + const uint32 blocks_x = (width + 3U) >> 2U; + const uint32 blocks_y = (height + 3U) >> 2U; + const uint32 block_size = m_pHeader->m_format == cCRNFmtDXT1 || m_pHeader->m_format == cCRNFmtDXT5A || m_pHeader->m_format == cCRNFmtETC1 || m_pHeader->m_format == cCRNFmtETC2 ? 8 : 16; + + uint32 minimal_row_pitch = block_size * blocks_x; + if (!row_pitch_in_bytes) + row_pitch_in_bytes = minimal_row_pitch; + else if ((row_pitch_in_bytes < minimal_row_pitch) || (row_pitch_in_bytes & 3)) + return false; + if (dst_size_in_bytes < row_pitch_in_bytes * blocks_y) + return false; + + if (!m_codec.start_decoding(static_cast(pSrc), src_size_in_bytes)) + return false; + + bool status = false; + switch (m_pHeader->m_format) { + case cCRNFmtDXT1: + status = unpack_dxt1((uint8**)pDst, row_pitch_in_bytes, blocks_x, blocks_y); + break; + case cCRNFmtDXT5: + case cCRNFmtDXT5_CCxY: + case cCRNFmtDXT5_xGBR: + case cCRNFmtDXT5_AGBR: + case cCRNFmtDXT5_xGxR: + status = unpack_dxt5((uint8**)pDst, row_pitch_in_bytes, blocks_x, blocks_y); + break; + case cCRNFmtDXT5A: + status = unpack_dxt5a((uint8**)pDst, row_pitch_in_bytes, blocks_x, blocks_y); + break; + case cCRNFmtDXN_XY: + case cCRNFmtDXN_YX: + status = unpack_dxn((uint8**)pDst, row_pitch_in_bytes, blocks_x, blocks_y); + break; + case cCRNFmtETC1: + status = unpack_etc1((uint8**)pDst, row_pitch_in_bytes, blocks_x, blocks_y); + break; + case cCRNFmtETC2: + status = unpack_etc1((uint8**)pDst, row_pitch_in_bytes, blocks_x, blocks_y); + break; + case cCRNFmtETC2A: + status = unpack_etc2a((uint8**)pDst, row_pitch_in_bytes, blocks_x, blocks_y); + break; + default: + return false; + } + if (!status) + return false; + + m_codec.stop_decoding(); + return true; + } + + inline const void* get_data() const { return m_pData; } + inline uint32 get_data_size() const { return m_data_size; } + + private: + enum { cMagicValue = 0x1EF9CABD }; + uint32 m_magic; + + const uint8* m_pData; + uint32 m_data_size; + const crn_header* m_pHeader; + + symbol_codec m_codec; + + static_huffman_data_model m_reference_encoding_dm; + static_huffman_data_model m_endpoint_delta_dm[2]; + static_huffman_data_model m_selector_delta_dm[2]; + + crnd::vector m_color_endpoints; + crnd::vector m_color_selectors; + + crnd::vector m_alpha_endpoints; + crnd::vector m_alpha_selectors; + + struct block_buffer_element { + uint16 endpoint_reference; + uint16 color_endpoint_index; + uint16 alpha0_endpoint_index; + uint16 alpha1_endpoint_index; + }; + crnd::vector m_block_buffer; + + bool init_tables() { + if (!m_codec.start_decoding(m_pData + m_pHeader->m_tables_ofs, m_pHeader->m_tables_size)) + return false; + + if (!m_codec.decode_receive_static_data_model(m_reference_encoding_dm)) + return false; + + if ((!m_pHeader->m_color_endpoints.m_num) && (!m_pHeader->m_alpha_endpoints.m_num)) + return false; + + if (m_pHeader->m_color_endpoints.m_num) { + if (!m_codec.decode_receive_static_data_model(m_endpoint_delta_dm[0])) + return false; + if (!m_codec.decode_receive_static_data_model(m_selector_delta_dm[0])) + return false; + } + + if (m_pHeader->m_alpha_endpoints.m_num) { + if (!m_codec.decode_receive_static_data_model(m_endpoint_delta_dm[1])) + return false; + if (!m_codec.decode_receive_static_data_model(m_selector_delta_dm[1])) + return false; + } + + m_codec.stop_decoding(); + + return true; + } + + bool decode_palettes() { + if (m_pHeader->m_color_endpoints.m_num) { + if (!decode_color_endpoints()) + return false; + if (!decode_color_selectors()) + return false; + } + + if (m_pHeader->m_alpha_endpoints.m_num) { + if (!decode_alpha_endpoints()) + return false; + if (!(m_pHeader->m_format == cCRNFmtETC2A ? decode_alpha_selectors_etc() : decode_alpha_selectors())) + return false; + } + + return true; + } + + bool decode_color_endpoints() { + const uint32 num_color_endpoints = m_pHeader->m_color_endpoints.m_num; + const bool has_etc_color_blocks = m_pHeader->m_format == cCRNFmtETC1 || m_pHeader->m_format == cCRNFmtETC2 || m_pHeader->m_format == cCRNFmtETC2A; + + if (!m_color_endpoints.resize(num_color_endpoints)) + return false; + + if (!m_codec.start_decoding(m_pData + m_pHeader->m_color_endpoints.m_ofs, m_pHeader->m_color_endpoints.m_size)) + return false; + + static_huffman_data_model dm[2]; + for (uint32 i = 0; i < (has_etc_color_blocks ? 1 : 2); i++) + if (!m_codec.decode_receive_static_data_model(dm[i])) + return false; + + uint32 a = 0, b = 0, c = 0; + uint32 d = 0, e = 0, f = 0; + + uint32* CRND_RESTRICT pDst = &m_color_endpoints[0]; + + for (uint32 i = 0; i < num_color_endpoints; i++) { + if (has_etc_color_blocks) { + for (b = 0; b < 32; b += 8) + a += m_codec.decode(dm[0]) << b; + *pDst++ = a &= 0x1F1F1F1F; + } else { + a = (a + m_codec.decode(dm[0])) & 31; + b = (b + m_codec.decode(dm[1])) & 63; + c = (c + m_codec.decode(dm[0])) & 31; + d = (d + m_codec.decode(dm[0])) & 31; + e = (e + m_codec.decode(dm[1])) & 63; + f = (f + m_codec.decode(dm[0])) & 31; + *pDst++ = c | (b << 5U) | (a << 11U) | (f << 16U) | (e << 21U) | (d << 27U); + } + } + + m_codec.stop_decoding(); + + return true; + } + + bool decode_color_selectors() { + const bool has_etc_color_blocks = m_pHeader->m_format == cCRNFmtETC1 || m_pHeader->m_format == cCRNFmtETC2 || m_pHeader->m_format == cCRNFmtETC2A; + m_codec.start_decoding(m_pData + m_pHeader->m_color_selectors.m_ofs, m_pHeader->m_color_selectors.m_size); + static_huffman_data_model dm; + m_codec.decode_receive_static_data_model(dm); + m_color_selectors.resize(m_pHeader->m_color_selectors.m_num << (has_etc_color_blocks ? 1 : 0)); + for (uint32 s = 0, i = 0; i < m_pHeader->m_color_selectors.m_num; i++) { + for (uint32 j = 0; j < 32; j += 4) + s ^= m_codec.decode(dm) << j; + if (has_etc_color_blocks) { + for (uint32 selector = (~s & 0xAAAAAAAA) | (~(s ^ s >> 1) & 0x55555555), t = 8, h = 0; h < 4; h++, t -= 15) { + for (uint32 w = 0; w < 4; w++, t += 4) { + uint32 s0 = selector >> (w << 3 | h << 1); + m_color_selectors[i << 1] |= ((s0 >> 1 & 1) | (s0 & 1) << 16) << (t & 15); + uint32 s1 = selector >> (h << 3 | w << 1); + m_color_selectors[i << 1 | 1] |= ((s1 >> 1 & 1) | (s1 & 1) << 16) << (t & 15); + } + } + } else { + m_color_selectors[i] = ((s ^ s << 1) & 0xAAAAAAAA) | (s >> 1 & 0x55555555); + } + } + m_codec.stop_decoding(); + return true; + } + + bool decode_alpha_endpoints() { + const uint32 num_alpha_endpoints = m_pHeader->m_alpha_endpoints.m_num; + + if (!m_codec.start_decoding(m_pData + m_pHeader->m_alpha_endpoints.m_ofs, m_pHeader->m_alpha_endpoints.m_size)) + return false; + + static_huffman_data_model dm; + if (!m_codec.decode_receive_static_data_model(dm)) + return false; + + if (!m_alpha_endpoints.resize(num_alpha_endpoints)) + return false; + + uint16* CRND_RESTRICT pDst = &m_alpha_endpoints[0]; + uint32 a = 0, b = 0; + + for (uint32 i = 0; i < num_alpha_endpoints; i++) { + a = (a + m_codec.decode(dm)) & 255; + b = (b + m_codec.decode(dm)) & 255; + *pDst++ = (uint16)(a | (b << 8)); + } + + m_codec.stop_decoding(); + + return true; + } + + bool decode_alpha_selectors() { + m_codec.start_decoding(m_pData + m_pHeader->m_alpha_selectors.m_ofs, m_pHeader->m_alpha_selectors.m_size); + static_huffman_data_model dm; + m_codec.decode_receive_static_data_model(dm); + m_alpha_selectors.resize(m_pHeader->m_alpha_selectors.m_num * 3); + uint8 dxt5_from_linear[64]; + for (uint32 i = 0; i < 64; i++) + dxt5_from_linear[i] = g_dxt5_from_linear[i & 7] | g_dxt5_from_linear[i >> 3] << 3; + for (uint32 s0_linear = 0, s1_linear = 0, i = 0; i < m_alpha_selectors.size();) { + uint32 s0 = 0, s1 = 0; + for (uint32 j = 0; j < 24; s0 |= dxt5_from_linear[s0_linear >> j & 0x3F] << j, j += 6) + s0_linear ^= m_codec.decode(dm) << j; + for (uint32 j = 0; j < 24; s1 |= dxt5_from_linear[s1_linear >> j & 0x3F] << j, j += 6) + s1_linear ^= m_codec.decode(dm) << j; + m_alpha_selectors[i++] = s0; + m_alpha_selectors[i++] = s0 >> 16 | s1 << 8; + m_alpha_selectors[i++] = s1 >> 8; + } + m_codec.stop_decoding(); + return true; + } + + bool decode_alpha_selectors_etc() { + m_codec.start_decoding(m_pData + m_pHeader->m_alpha_selectors.m_ofs, m_pHeader->m_alpha_selectors.m_size); + static_huffman_data_model dm; + m_codec.decode_receive_static_data_model(dm); + m_alpha_selectors.resize(m_pHeader->m_alpha_selectors.m_num * 6); + uint8 s_linear[8] = {}; + uint8* data = (uint8*)m_alpha_selectors.begin(); + for (uint i = 0; i < m_alpha_selectors.size(); i += 6, data += 12) { + for (uint s_group = 0, p = 0; p < 16; p++) { + s_group = p & 1 ? s_group >> 3 : s_linear[p >> 1] ^= m_codec.decode(dm); + uint8 s = s_group & 7; + if (s <= 3) + s = 3 - s; + uint8 d = 3 * (p + 1); + uint8 byte_offset = d >> 3; + uint8 bit_offset = d & 7; + data[byte_offset] |= s << (8 - bit_offset); + if (bit_offset < 3) + data[byte_offset - 1] |= s >> bit_offset; + d += 9 * ((p & 3) - (p >> 2)); + byte_offset = d >> 3; + bit_offset = d & 7; + data[byte_offset + 6] |= s << (8 - bit_offset); + if (bit_offset < 3) + data[byte_offset + 5] |= s >> bit_offset; + } + } + m_codec.stop_decoding(); + return true; + } + + static inline uint32 tiled_offset_2d_outer(uint32 y, uint32 AlignedWidth, uint32 LogBpp) { + uint32 Macro = ((y >> 5) * (AlignedWidth >> 5)) << (LogBpp + 7); + uint32 Micro = ((y & 6) << 2) << LogBpp; + + return Macro + + ((Micro & ~15) << 1) + + (Micro & 15) + + ((y & 8) << (3 + LogBpp)) + ((y & 1) << 4); + } + + static inline uint32 tiled_offset_2d_inner(uint32 x, uint32 y, uint32 LogBpp, uint32 BaseOffset) { + uint32 Macro = (x >> 5) << (LogBpp + 7); + uint32 Micro = (x & 7) << LogBpp; + uint32 Offset = BaseOffset + Macro + ((Micro & ~15) << 1) + (Micro & 15); + + return ((Offset & ~511) << 3) + ((Offset & 448) << 2) + (Offset & 63) + + ((y & 16) << 7) + + (((((y & 8) >> 2) + (x >> 3)) & 3) << 6); + } + + static inline void limit(uint& x, uint n) { + int v = x - n; + int msk = (v >> 31); + x = (x & msk) | (v & ~msk); + } + + bool unpack_dxt1(uint8** pDst, uint32 output_pitch_in_bytes, uint32 output_width, uint32 output_height) { + const uint32 num_color_endpoints = m_color_endpoints.size(); + const uint32 width = (output_width + 1) & ~1; + const uint32 height = (output_height + 1) & ~1; + const int32 delta_pitch_in_dwords = (output_pitch_in_bytes >> 2) - (width << 1); + + if (m_block_buffer.size() < width) + m_block_buffer.resize(width); + + uint32 color_endpoint_index = 0; + uint8 reference_group = 0; + + for (uint32 f = 0; f < m_pHeader->m_faces; f++) { + uint32* pData = (uint32*)pDst[f]; + for (uint32 y = 0; y < height; y++, pData += delta_pitch_in_dwords) { + bool visible = y < output_height; + for (uint32 x = 0; x < width; x++, pData += 2) { + visible = visible && x < output_width; + if (!(y & 1) && !(x & 1)) + reference_group = m_codec.decode(m_reference_encoding_dm); + block_buffer_element &buffer = m_block_buffer[x]; + uint8 endpoint_reference; + if (y & 1) { + endpoint_reference = buffer.endpoint_reference; + } else { + endpoint_reference = reference_group & 3; + reference_group >>= 2; + buffer.endpoint_reference = reference_group & 3; + reference_group >>= 2; + } + if (!endpoint_reference) { + color_endpoint_index += m_codec.decode(m_endpoint_delta_dm[0]); + if (color_endpoint_index >= num_color_endpoints) + color_endpoint_index -= num_color_endpoints; + buffer.color_endpoint_index = color_endpoint_index; + } else if (endpoint_reference == 1) { + buffer.color_endpoint_index = color_endpoint_index; + } else { + color_endpoint_index = buffer.color_endpoint_index; + } + uint32 color_selector_index = m_codec.decode(m_selector_delta_dm[0]); + if (visible) { + pData[0] = m_color_endpoints[color_endpoint_index]; + pData[1] = m_color_selectors[color_selector_index]; + } + } + } + } + return true; + } + + bool unpack_dxt5(uint8** pDst, uint32 row_pitch_in_bytes, uint32 output_width, uint32 output_height) { + const uint32 num_color_endpoints = m_color_endpoints.size(); + const uint32 num_alpha_endpoints = m_alpha_endpoints.size(); + const uint32 width = (output_width + 1) & ~1; + const uint32 height = (output_height + 1) & ~1; + const int32 delta_pitch_in_dwords = (row_pitch_in_bytes >> 2) - (width << 2); + + if (m_block_buffer.size() < width) + m_block_buffer.resize(width); + + uint32 color_endpoint_index = 0; + uint32 alpha0_endpoint_index = 0; + uint8 reference_group = 0; + + for (uint32 f = 0; f < m_pHeader->m_faces; f++) { + uint32* pData = (uint32*)pDst[f]; + for (uint32 y = 0; y < height; y++, pData += delta_pitch_in_dwords) { + bool visible = y < output_height; + for (uint32 x = 0; x < width; x++, pData += 4) { + visible = visible && x < output_width; + if (!(y & 1) && !(x & 1)) + reference_group = m_codec.decode(m_reference_encoding_dm); + block_buffer_element &buffer = m_block_buffer[x]; + uint8 endpoint_reference; + if (y & 1) { + endpoint_reference = buffer.endpoint_reference; + } else { + endpoint_reference = reference_group & 3; + reference_group >>= 2; + buffer.endpoint_reference = reference_group & 3; + reference_group >>= 2; + } + if (!endpoint_reference) { + color_endpoint_index += m_codec.decode(m_endpoint_delta_dm[0]); + if (color_endpoint_index >= num_color_endpoints) + color_endpoint_index -= num_color_endpoints; + buffer.color_endpoint_index = color_endpoint_index; + alpha0_endpoint_index += m_codec.decode(m_endpoint_delta_dm[1]); + if (alpha0_endpoint_index >= num_alpha_endpoints) + alpha0_endpoint_index -= num_alpha_endpoints; + buffer.alpha0_endpoint_index = alpha0_endpoint_index; + } else if (endpoint_reference == 1) { + buffer.color_endpoint_index = color_endpoint_index; + buffer.alpha0_endpoint_index = alpha0_endpoint_index; + } else { + color_endpoint_index = buffer.color_endpoint_index; + alpha0_endpoint_index = buffer.alpha0_endpoint_index; + } + uint32 color_selector_index = m_codec.decode(m_selector_delta_dm[0]); + uint32 alpha0_selector_index = m_codec.decode(m_selector_delta_dm[1]); + if (visible) { + const uint16* pAlpha0_selectors = &m_alpha_selectors[alpha0_selector_index * 3]; + pData[0] = m_alpha_endpoints[alpha0_endpoint_index] | (pAlpha0_selectors[0] << 16); + pData[1] = pAlpha0_selectors[1] | (pAlpha0_selectors[2] << 16); + pData[2] = m_color_endpoints[color_endpoint_index]; + pData[3] = m_color_selectors[color_selector_index]; + } + } + } + } + return true; + } + + bool unpack_dxn(uint8** pDst, uint32 row_pitch_in_bytes, uint32 output_width, uint32 output_height) { + const uint32 num_alpha_endpoints = m_alpha_endpoints.size(); + const uint32 width = (output_width + 1) & ~1; + const uint32 height = (output_height + 1) & ~1; + const int32 delta_pitch_in_dwords = (row_pitch_in_bytes >> 2) - (width << 2); + + if (m_block_buffer.size() < width) + m_block_buffer.resize(width); + + uint32 alpha0_endpoint_index = 0; + uint32 alpha1_endpoint_index = 0; + uint8 reference_group = 0; + + for (uint32 f = 0; f < m_pHeader->m_faces; f++) { + uint32* pData = (uint32*)pDst[f]; + for (uint32 y = 0; y < height; y++, pData += delta_pitch_in_dwords) { + bool visible = y < output_height; + for (uint32 x = 0; x < width; x++, pData += 4) { + visible = visible && x < output_width; + if (!(y & 1) && !(x & 1)) + reference_group = m_codec.decode(m_reference_encoding_dm); + block_buffer_element &buffer = m_block_buffer[x]; + uint8 endpoint_reference; + if (y & 1) { + endpoint_reference = buffer.endpoint_reference; + } else { + endpoint_reference = reference_group & 3; + reference_group >>= 2; + buffer.endpoint_reference = reference_group & 3; + reference_group >>= 2; + } + if (!endpoint_reference) { + alpha0_endpoint_index += m_codec.decode(m_endpoint_delta_dm[1]); + if (alpha0_endpoint_index >= num_alpha_endpoints) + alpha0_endpoint_index -= num_alpha_endpoints; + buffer.alpha0_endpoint_index = alpha0_endpoint_index; + alpha1_endpoint_index += m_codec.decode(m_endpoint_delta_dm[1]); + if (alpha1_endpoint_index >= num_alpha_endpoints) + alpha1_endpoint_index -= num_alpha_endpoints; + buffer.alpha1_endpoint_index = alpha1_endpoint_index; + } else if (endpoint_reference == 1) { + buffer.alpha0_endpoint_index = alpha0_endpoint_index; + buffer.alpha1_endpoint_index = alpha1_endpoint_index; + } else { + alpha0_endpoint_index = buffer.alpha0_endpoint_index; + alpha1_endpoint_index = buffer.alpha1_endpoint_index; + } + uint32 alpha0_selector_index = m_codec.decode(m_selector_delta_dm[1]); + uint32 alpha1_selector_index = m_codec.decode(m_selector_delta_dm[1]); + if (visible) { + const uint16* pAlpha0_selectors = &m_alpha_selectors[alpha0_selector_index * 3]; + const uint16* pAlpha1_selectors = &m_alpha_selectors[alpha1_selector_index * 3]; + pData[0] = m_alpha_endpoints[alpha0_endpoint_index] | (pAlpha0_selectors[0] << 16); + pData[1] = pAlpha0_selectors[1] | (pAlpha0_selectors[2] << 16); + pData[2] = m_alpha_endpoints[alpha1_endpoint_index] | (pAlpha1_selectors[0] << 16); + pData[3] = pAlpha1_selectors[1] | (pAlpha1_selectors[2] << 16); + } + } + } + } + return true; + } + + bool unpack_dxt5a(uint8** pDst, uint32 row_pitch_in_bytes, uint32 output_width, uint32 output_height) { + const uint32 num_alpha_endpoints = m_alpha_endpoints.size(); + const uint32 width = (output_width + 1) & ~1; + const uint32 height = (output_height + 1) & ~1; + const int32 delta_pitch_in_dwords = (row_pitch_in_bytes >> 2) - (width << 1); + + if (m_block_buffer.size() < width) + m_block_buffer.resize(width); + + uint32 alpha0_endpoint_index = 0; + uint8 reference_group = 0; + + for (uint32 f = 0; f < m_pHeader->m_faces; f++) { + uint32* pData = (uint32*)pDst[f]; + for (uint32 y = 0; y < height; y++, pData += delta_pitch_in_dwords) { + bool visible = y < output_height; + for (uint32 x = 0; x < width; x++, pData += 2) { + visible = visible && x < output_width; + if (!(y & 1) && !(x & 1)) + reference_group = m_codec.decode(m_reference_encoding_dm); + block_buffer_element &buffer = m_block_buffer[x]; + uint8 endpoint_reference; + if (y & 1) { + endpoint_reference = buffer.endpoint_reference; + } else { + endpoint_reference = reference_group & 3; + reference_group >>= 2; + buffer.endpoint_reference = reference_group & 3; + reference_group >>= 2; + } + if (!endpoint_reference) { + alpha0_endpoint_index += m_codec.decode(m_endpoint_delta_dm[1]); + if (alpha0_endpoint_index >= num_alpha_endpoints) + alpha0_endpoint_index -= num_alpha_endpoints; + buffer.alpha0_endpoint_index = alpha0_endpoint_index; + } else if (endpoint_reference == 1) { + buffer.alpha0_endpoint_index = alpha0_endpoint_index; + } else { + alpha0_endpoint_index = buffer.alpha0_endpoint_index; + } + uint32 alpha0_selector_index = m_codec.decode(m_selector_delta_dm[1]); + if (visible) { + const uint16* pAlpha0_selectors = &m_alpha_selectors[alpha0_selector_index * 3]; + pData[0] = m_alpha_endpoints[alpha0_endpoint_index] | (pAlpha0_selectors[0] << 16); + pData[1] = pAlpha0_selectors[1] | (pAlpha0_selectors[2] << 16); + } + } + } + } + return true; + } + + bool unpack_etc1(uint8** pDst, uint32 output_pitch_in_bytes, uint32 output_width, uint32 output_height) { + const uint32 num_color_endpoints = m_color_endpoints.size(); + const uint32 width = (output_width + 1) & ~1; + const uint32 height = (output_height + 1) & ~1; + const int32 delta_pitch_in_dwords = (output_pitch_in_bytes >> 2) - (width << 1); + + if (m_block_buffer.size() < width << 1) + m_block_buffer.resize(width << 1); + + uint32 color_endpoint_index = 0, diagonal_color_endpoint_index = 0; + uint8 reference_group = 0; + + for (uint32 f = 0; f < m_pHeader->m_faces; f++) { + uint32* pData = (uint32*)pDst[f]; + for (uint32 y = 0; y < height; y++, pData += delta_pitch_in_dwords) { + bool visible = y < output_height; + for (uint32 x = 0; x < width; x++, pData += 2) { + visible = visible && x < output_width; + block_buffer_element &buffer = m_block_buffer[x << 1]; + uint8 endpoint_reference, block_endpoint[4], e0[4], e1[4]; + if (y & 1) { + endpoint_reference = buffer.endpoint_reference; + } else { + reference_group = m_codec.decode(m_reference_encoding_dm); + endpoint_reference = (reference_group & 3) | (reference_group >> 2 & 12); + buffer.endpoint_reference = (reference_group >> 2 & 3) | (reference_group >> 4 & 12); + } + if (!(endpoint_reference & 3)) { + color_endpoint_index += m_codec.decode(m_endpoint_delta_dm[0]); + if (color_endpoint_index >= num_color_endpoints) + color_endpoint_index -= num_color_endpoints; + buffer.color_endpoint_index = color_endpoint_index; + } else if ((endpoint_reference & 3) == 1) { + buffer.color_endpoint_index = color_endpoint_index; + } else if ((endpoint_reference & 3) == 3) { + buffer.color_endpoint_index = color_endpoint_index = diagonal_color_endpoint_index; + } else { + color_endpoint_index = buffer.color_endpoint_index; + } + endpoint_reference >>= 2; + *(uint32*)&e0 = m_color_endpoints[color_endpoint_index]; + uint32 selector_index = m_codec.decode(m_selector_delta_dm[0]); + if (endpoint_reference) { + color_endpoint_index += m_codec.decode(m_endpoint_delta_dm[0]); + if (color_endpoint_index >= num_color_endpoints) + color_endpoint_index -= num_color_endpoints; + } + diagonal_color_endpoint_index = m_block_buffer[x << 1 | 1].color_endpoint_index; + m_block_buffer[x << 1 | 1].color_endpoint_index = color_endpoint_index; + *(uint32*)&e1 = m_color_endpoints[color_endpoint_index]; + if (visible) { + uint32 flip = endpoint_reference >> 1 ^ 1, diff = 1; + for (uint c = 0; diff && c < 3; c++) + diff = e0[c] + 3 >= e1[c] && e1[c] + 4 >= e0[c] ? diff : 0; + for (uint c = 0; c < 3; c++) + block_endpoint[c] = diff ? e0[c] << 3 | ((e1[c] - e0[c]) & 7) : (e0[c] << 3 & 0xF0) | e1[c] >> 1; + block_endpoint[3] = e0[3] << 5 | e1[3] << 2 | diff << 1 | flip; + pData[0] = *(uint32*)&block_endpoint; + pData[1] = m_color_selectors[selector_index << 1 | flip]; + } + } + } + } + return true; + } + + bool unpack_etc2a(uint8** pDst, uint32 output_pitch_in_bytes, uint32 output_width, uint32 output_height) { + const uint32 num_color_endpoints = m_color_endpoints.size(); + const uint32 num_alpha_endpoints = m_alpha_endpoints.size(); + const uint32 width = (output_width + 1) & ~1; + const uint32 height = (output_height + 1) & ~1; + const int32 delta_pitch_in_dwords = (output_pitch_in_bytes >> 2) - (width << 2); + + if (m_block_buffer.size() < width << 1) + m_block_buffer.resize(width << 1); + + uint32 color_endpoint_index = 0, diagonal_color_endpoint_index = 0, alpha0_endpoint_index = 0, diagonal_alpha0_endpoint_index = 0; + uint8 reference_group = 0; + + for (uint32 f = 0; f < m_pHeader->m_faces; f++) { + uint32* pData = (uint32*)pDst[f]; + for (uint32 y = 0; y < height; y++, pData += delta_pitch_in_dwords) { + bool visible = y < output_height; + for (uint32 x = 0; x < width; x++, pData += 4) { + visible = visible && x < output_width; + block_buffer_element &buffer = m_block_buffer[x << 1]; + uint8 endpoint_reference, block_endpoint[4], e0[4], e1[4]; + if (y & 1) { + endpoint_reference = buffer.endpoint_reference; + } else { + reference_group = m_codec.decode(m_reference_encoding_dm); + endpoint_reference = (reference_group & 3) | (reference_group >> 2 & 12); + buffer.endpoint_reference = (reference_group >> 2 & 3) | (reference_group >> 4 & 12); + } + if (!(endpoint_reference & 3)) { + color_endpoint_index += m_codec.decode(m_endpoint_delta_dm[0]); + if (color_endpoint_index >= num_color_endpoints) + color_endpoint_index -= num_color_endpoints; + alpha0_endpoint_index += m_codec.decode(m_endpoint_delta_dm[1]); + if (alpha0_endpoint_index >= num_alpha_endpoints) + alpha0_endpoint_index -= num_alpha_endpoints; + buffer.color_endpoint_index = color_endpoint_index; + buffer.alpha0_endpoint_index = alpha0_endpoint_index; + } else if ((endpoint_reference & 3) == 1) { + buffer.color_endpoint_index = color_endpoint_index; + buffer.alpha0_endpoint_index = alpha0_endpoint_index; + } else if ((endpoint_reference & 3) == 3) { + buffer.color_endpoint_index = color_endpoint_index = diagonal_color_endpoint_index; + buffer.alpha0_endpoint_index = alpha0_endpoint_index = diagonal_alpha0_endpoint_index; + } else { + color_endpoint_index = buffer.color_endpoint_index; + alpha0_endpoint_index = buffer.alpha0_endpoint_index; + } + endpoint_reference >>= 2; + *(uint32*)&e0 = m_color_endpoints[color_endpoint_index]; + uint32 color_selector_index = m_codec.decode(m_selector_delta_dm[0]); + uint32 alpha0_selector_index = m_codec.decode(m_selector_delta_dm[1]); + if (endpoint_reference) { + color_endpoint_index += m_codec.decode(m_endpoint_delta_dm[0]); + if (color_endpoint_index >= num_color_endpoints) + color_endpoint_index -= num_color_endpoints; + } + *(uint32*)&e1 = m_color_endpoints[color_endpoint_index]; + diagonal_color_endpoint_index = m_block_buffer[x << 1 | 1].color_endpoint_index; + diagonal_alpha0_endpoint_index = m_block_buffer[x << 1 | 1].alpha0_endpoint_index; + m_block_buffer[x << 1 | 1].color_endpoint_index = color_endpoint_index; + m_block_buffer[x << 1 | 1].alpha0_endpoint_index = alpha0_endpoint_index; + if (visible) { + uint32 flip = endpoint_reference >> 1 ^ 1, diff = 1; + for (uint c = 0; diff && c < 3; c++) + diff = e0[c] + 3 >= e1[c] && e1[c] + 4 >= e0[c] ? diff : 0; + for (uint c = 0; c < 3; c++) + block_endpoint[c] = diff ? e0[c] << 3 | ((e1[c] - e0[c]) & 7) : (e0[c] << 3 & 0xF0) | e1[c] >> 1; + block_endpoint[3] = e0[3] << 5 | e1[3] << 2 | diff << 1 | flip; + const uint16* pAlpha0_selectors = &m_alpha_selectors[alpha0_selector_index * 6 + (flip ? 3 : 0)]; + pData[0] = m_alpha_endpoints[alpha0_endpoint_index] | pAlpha0_selectors[0] << 16; + pData[1] = pAlpha0_selectors[1] | pAlpha0_selectors[2] << 16; + pData[2] = *(uint32*)&block_endpoint; + pData[3] = m_color_selectors[color_selector_index << 1 | flip]; + } + } + } + } + return true; + } + +}; + +crnd_unpack_context crnd_unpack_begin(const void* pData, uint32 data_size) { + if ((!pData) || (data_size < cCRNHeaderMinSize)) + return NULL; + + crn_unpacker* p = crnd_new(); + if (!p) + return NULL; + + if (!p->init(pData, data_size)) { + crnd_delete(p); + return NULL; + } + + return p; +} + +bool crnd_get_data(crnd_unpack_context pContext, const void** ppData, uint32* pData_size) { + if (!pContext) + return false; + + crn_unpacker* pUnpacker = static_cast(pContext); + + if (!pUnpacker->is_valid()) + return false; + + if (ppData) + *ppData = pUnpacker->get_data(); + + if (pData_size) + *pData_size = pUnpacker->get_data_size(); + + return true; +} + +bool crnd_unpack_level( + crnd_unpack_context pContext, + void** pDst, uint32 dst_size_in_bytes, uint32 row_pitch_in_bytes, + uint32 level_index) { + if ((!pContext) || (!pDst) || (dst_size_in_bytes < 8U) || (level_index >= cCRNMaxLevels)) + return false; + + crn_unpacker* pUnpacker = static_cast(pContext); + + if (!pUnpacker->is_valid()) + return false; + + return pUnpacker->unpack_level(pDst, dst_size_in_bytes, row_pitch_in_bytes, level_index); +} + +bool crnd_unpack_level_segmented( + crnd_unpack_context pContext, + const void* pSrc, uint32 src_size_in_bytes, + void** pDst, uint32 dst_size_in_bytes, uint32 row_pitch_in_bytes, + uint32 level_index) { + if ((!pContext) || (!pSrc) || (!pDst) || (dst_size_in_bytes < 8U) || (level_index >= cCRNMaxLevels)) + return false; + + crn_unpacker* pUnpacker = static_cast(pContext); + + if (!pUnpacker->is_valid()) + return false; + + return pUnpacker->unpack_level(pSrc, src_size_in_bytes, pDst, dst_size_in_bytes, row_pitch_in_bytes, level_index); +} + +bool crnd_unpack_end(crnd_unpack_context pContext) { + if (!pContext) + return false; + + crn_unpacker* pUnpacker = static_cast(pContext); + + if (!pUnpacker->is_valid()) + return false; + + crnd_delete(pUnpacker); + + return true; +} + +} // namespace crnd + +#endif // CRND_INCLUDE_CRND_H + +//------------------------------------------------------------------------------ +// +// crn_decomp.h uses the ZLIB license: +// http://opensource.org/licenses/Zlib +// +// Copyright (c) 2010-2016 Richard Geldreich, Jr. and Binomial LLC +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//------------------------------------------------------------------------------ diff --git a/libs/crunch/crn_defs.h b/libs/crunch/crn_defs.h new file mode 100644 index 00000000..d2cf024c --- /dev/null +++ b/libs/crunch/crn_defs.h @@ -0,0 +1,291 @@ +#ifndef CRND_INCLUDE_CRN_DEFS_H +#define CRND_INCLUDE_CRN_DEFS_H + +// Include crnlib.h (only to bring in some basic CRN-related types). +#include "crnlib.h" + +#define CRND_LIB_VERSION 104 +#define CRND_VERSION_STRING "01.04" + +#ifdef _DEBUG +#define CRND_BUILD_DEBUG +#else +#define CRND_BUILD_RELEASE +#endif + +// CRN decompression API +namespace crnd { +typedef unsigned char uint8; +typedef signed char int8; +typedef unsigned short uint16; +typedef signed short int16; +typedef unsigned int uint32; +typedef uint32 uint32; +typedef unsigned int uint; +typedef signed int int32; +#ifdef __GNUC__ +typedef unsigned long long uint64; +typedef long long int64; +#else +typedef unsigned __int64 uint64; +typedef signed __int64 int64; +#endif + +// The crnd library assumes all allocation blocks have at least CRND_MIN_ALLOC_ALIGNMENT alignment. +const uint32 CRND_MIN_ALLOC_ALIGNMENT = sizeof(uint32) * 2U; + +// realloc callback: +// Used to allocate, resize, or free memory blocks. +// If p is NULL, the realloc function attempts to allocate a block of at least size bytes. Returns NULL on out of memory. +// *pActual_size must be set to the actual size of the allocated block, which must be greater than or equal to the requested size. +// If p is not NULL, and size is 0, the realloc function frees the specified block, and always returns NULL. *pActual_size should be set to 0. +// If p is not NULL, and size is non-zero, the realloc function attempts to resize the specified block: +// If movable is false, the realloc function attempts to shrink or expand the block in-place. NULL is returned if the block cannot be resized in place, or if the +// underlying heap implementation doesn't support in-place resizing. Otherwise, the pointer to the original block is returned. +// If movable is true, it is permissible to move the block's contents if it cannot be resized in place. NULL is returned if the block cannot be resized in place, and there +// is not enough memory to relocate the block. +// In all cases, *pActual_size must be set to the actual size of the allocated block, whether it was successfully resized or not. +typedef void* (*crnd_realloc_func)(void* p, size_t size, size_t* pActual_size, bool movable, void* pUser_data); + +// msize callback: Returns the size of the memory block in bytes, or 0 if the pointer or block is invalid. +typedef size_t (*crnd_msize_func)(void* p, void* pUser_data); + +// crnd_set_memory_callbacks() - Use to override the crnd library's memory allocation functions. +// If any input parameters are NULL, the memory callback functions are reset to the default functions. +// The default functions call malloc(), free(), _msize(), _expand(), etc. +void crnd_set_memory_callbacks(crnd_realloc_func pRealloc, crnd_msize_func pMSize, void* pUser_data); + +struct crn_file_info { + inline crn_file_info() + : m_struct_size(sizeof(crn_file_info)) {} + + uint32 m_struct_size; + uint32 m_actual_data_size; + uint32 m_header_size; + uint32 m_total_palette_size; + uint32 m_tables_size; + uint32 m_levels; + uint32 m_level_compressed_size[cCRNMaxLevels]; + uint32 m_color_endpoint_palette_entries; + uint32 m_color_selector_palette_entries; + uint32 m_alpha_endpoint_palette_entries; + uint32 m_alpha_selector_palette_entries; +}; + +struct crn_texture_info { + inline crn_texture_info() + : m_struct_size(sizeof(crn_texture_info)) {} + + uint32 m_struct_size; + uint32 m_width; + uint32 m_height; + uint32 m_levels; + uint32 m_faces; + uint32 m_bytes_per_block; + uint32 m_userdata0; + uint32 m_userdata1; + crn_format m_format; +}; + +struct crn_level_info { + inline crn_level_info() + : m_struct_size(sizeof(crn_level_info)) {} + + uint32 m_struct_size; + uint32 m_width; + uint32 m_height; + uint32 m_faces; + uint32 m_blocks_x; + uint32 m_blocks_y; + uint32 m_bytes_per_block; + crn_format m_format; +}; + +// Returns the FOURCC format code corresponding to the specified CRN format. +uint32 crnd_crn_format_to_fourcc(crn_format fmt); + +// Returns the fundamental GPU format given a potentially swizzled DXT5 crn_format. +crn_format crnd_get_fundamental_dxt_format(crn_format fmt); + +// Returns the size of the crn_format in bits/texel (either 4 or 8). +uint32 crnd_get_crn_format_bits_per_texel(crn_format fmt); + +// Returns the number of bytes per DXTn block (8 or 16). +uint32 crnd_get_bytes_per_dxt_block(crn_format fmt); + +// Validates the entire file by checking the header and data CRC's. +// This is not something you want to be doing much! +// The crn_file_info.m_struct_size field must be set before calling this function. +bool crnd_validate_file(const void* pData, uint32 data_size, crn_file_info* pFile_info); + +// Retrieves texture information from the CRN file. +// The crn_texture_info.m_struct_size field must be set before calling this function. +bool crnd_get_texture_info(const void* pData, uint32 data_size, crn_texture_info* pTexture_info); + +// Retrieves mipmap level specific information from the CRN file. +// The crn_level_info.m_struct_size field must be set before calling this function. +bool crnd_get_level_info(const void* pData, uint32 data_size, uint32 level_index, crn_level_info* pLevel_info); + +// Transcode/unpack context handle. +typedef void* crnd_unpack_context; + +// crnd_unpack_begin() - Decompresses the texture's decoder tables and endpoint/selector palettes. +// Once you call this function, you may call crnd_unpack_level() to unpack one or more mip levels. +// Don't call this once per mip level (unless you absolutely must)! +// This function allocates enough memory to hold: Huffman decompression tables, and the endpoint/selector palettes (color and/or alpha). +// Worst case allocation is approx. 200k, assuming all palettes contain 8192 entries. +// pData must point to a buffer holding all of the compressed .CRN file data. +// This buffer must be stable until crnd_unpack_end() is called. +// Returns NULL if out of memory, or if any of the input parameters are invalid. +crnd_unpack_context crnd_unpack_begin(const void* pData, uint32 data_size); + +// Returns a pointer to the compressed .CRN data associated with a crnd_unpack_context. +// Returns false if any of the input parameters are invalid. +bool crnd_get_data(crnd_unpack_context pContext, const void** ppData, uint32* pData_size); + +// crnd_unpack_level() - Transcodes the specified mipmap level to a destination buffer in cached or write combined memory. +// pContext - Context created by a call to crnd_unpack_begin(). +// ppDst - A pointer to an array of 1 or 6 destination buffer pointers. Cubemaps require an array of 6 pointers, 2D textures require an array of 1 pointer. +// dst_size_in_bytes - Optional size of each destination buffer. Only used for debugging - OK to set to UINT32_MAX. +// row_pitch_in_bytes - The pitch in bytes from one row of DXT blocks to the next. Must be a multiple of 4. +// level_index - mipmap level index, where 0 is the largest/first level. +// Returns false if any of the input parameters, or the compressed stream, are invalid. +// This function does not allocate any memory. +bool crnd_unpack_level( + crnd_unpack_context pContext, + void** ppDst, uint32 dst_size_in_bytes, uint32 row_pitch_in_bytes, + uint32 level_index); + +// crnd_unpack_level_segmented() - Unpacks the specified mipmap level from a "segmented" CRN file. +// See the crnd_create_segmented_file() API below. +// Segmented files allow the user to control where the compressed mipmap data is stored. +bool crnd_unpack_level_segmented( + crnd_unpack_context pContext, + const void* pSrc, uint32 src_size_in_bytes, + void** ppDst, uint32 dst_size_in_bytes, uint32 row_pitch_in_bytes, + uint32 level_index); + +// crnd_unpack_end() - Frees the decompress tables and unpacked palettes associated with the specified unpack context. +// Returns false if the context is NULL, or if it points to an invalid context. +// This function frees all memory associated with the context. +bool crnd_unpack_end(crnd_unpack_context pContext); + +// The following API's allow the user to create "segmented" CRN files. A segmented file contains multiple pieces: +// - Base data: Header + compression tables +// - Level data: Individual mipmap levels +// This allows mipmap levels from multiple CRN files to be tightly packed together into single files. + +// Returns a pointer to the level's compressed data, and optionally returns the level's compressed data size if pSize is not NULL. +const void* crnd_get_level_data(const void* pData, uint32 data_size, uint32 level_index, uint32* pSize); + +// Returns the compressed size of the texture's header and compression tables (but no levels). +uint32 crnd_get_segmented_file_size(const void* pData, uint32 data_size); + +// Creates a "segmented" CRN texture from a normal CRN texture. The new texture will be created at pBase_data, and will be crnd_get_base_data_size() bytes long. +// base_data_size must be >= crnd_get_base_data_size(). +// The base data will contain the CRN header and compression tables, but no mipmap data. +bool crnd_create_segmented_file(const void* pData, uint32 data_size, void* pBase_data, uint base_data_size); + +} // namespace crnd + +// Low-level CRN file header cracking. +namespace crnd { +template +struct crn_packed_uint { + inline crn_packed_uint() {} + + inline crn_packed_uint(unsigned int val) { *this = val; } + + inline crn_packed_uint(const crn_packed_uint& other) { *this = other; } + + inline crn_packed_uint& operator=(const crn_packed_uint& rhs) { + if (this != &rhs) + memcpy(m_buf, rhs.m_buf, sizeof(m_buf)); + return *this; + } + + inline crn_packed_uint& operator=(unsigned int val) { + //CRND_ASSERT((N == 4U) || (val < (1U << (N * 8U)))); + + val <<= (8U * (4U - N)); + + for (unsigned int i = 0; i < N; i++) { + m_buf[i] = static_cast(val >> 24U); + val <<= 8U; + } + + return *this; + } + + inline operator unsigned int() const { + switch (N) { + case 1: + return m_buf[0]; + case 2: + return (m_buf[0] << 8U) | m_buf[1]; + case 3: + return (m_buf[0] << 16U) | (m_buf[1] << 8U) | (m_buf[2]); + default: + return (m_buf[0] << 24U) | (m_buf[1] << 16U) | (m_buf[2] << 8U) | (m_buf[3]); + } + } + + unsigned char m_buf[N]; +}; + +#pragma pack(push) +#pragma pack(1) +struct crn_palette { + crn_packed_uint<3> m_ofs; + crn_packed_uint<3> m_size; + crn_packed_uint<2> m_num; +}; + +enum crn_header_flags { + // If set, the compressed mipmap level data is not located after the file's base data - it will be separately managed by the user instead. + cCRNHeaderFlagSegmented = 1 +}; + +struct crn_header { + enum { cCRNSigValue = ('H' << 8) | 'x' }; + + crn_packed_uint<2> m_sig; + crn_packed_uint<2> m_header_size; + crn_packed_uint<2> m_header_crc16; + + crn_packed_uint<4> m_data_size; + crn_packed_uint<2> m_data_crc16; + + crn_packed_uint<2> m_width; + crn_packed_uint<2> m_height; + + crn_packed_uint<1> m_levels; + crn_packed_uint<1> m_faces; + + crn_packed_uint<1> m_format; + crn_packed_uint<2> m_flags; + + crn_packed_uint<4> m_reserved; + crn_packed_uint<4> m_userdata0; + crn_packed_uint<4> m_userdata1; + + crn_palette m_color_endpoints; + crn_palette m_color_selectors; + + crn_palette m_alpha_endpoints; + crn_palette m_alpha_selectors; + + crn_packed_uint<2> m_tables_size; + crn_packed_uint<3> m_tables_ofs; + + // m_level_ofs[] is actually an array of offsets: m_level_ofs[m_levels] + crn_packed_uint<4> m_level_ofs[1]; +}; + +const unsigned int cCRNHeaderMinSize = 62U; + +#pragma pack(pop) + +} // namespace crnd + +#endif // CRND_INCLUDE_CRN_DEFS_H diff --git a/libs/crunch/crn_rgba.cpp b/libs/crunch/crn_rgba.cpp new file mode 100644 index 00000000..0280dad9 --- /dev/null +++ b/libs/crunch/crn_rgba.cpp @@ -0,0 +1,128 @@ +/* + Copyright (C) 2018, Unvanquished Developers + All Rights Reserved. + + This file is part of NetRadiant. + + NetRadiant is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + NetRadiant is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with NetRadiant; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include "crn_rgba.h" + +#include + +#include + + +#include "ddslib.h" +#include "crn_decomp.h" + +int LittleLong(int l) { +#if GDEF_ARCH_ENDIAN_BIG + std::reverse(reinterpret_cast( &l ), reinterpret_cast( &l ) + sizeof(int)); +#endif + return l; +} + +// Sets `x` and `y` to the width and height of the input crn image. Returns false if there is an +// error reading the image. +extern "C" int GetCRNImageSize(const void *buffer, int length, int *x, int *y) { + crnd::crn_texture_info ti; + if(!crnd::crnd_get_texture_info(buffer, length, &ti) || + // Ensure we are not trying to load a cubemap (which has 6 faces...) + (ti.m_faces != 1) ) { + return false; + } + if (x) *x = ti.m_width; + if (y) *y = ti.m_height; + return true; +} + +// Converts a .crn file to RGBA. Stores the pixels in outBuf. Use GetCRNImageSize to get the image +// size to determine how big outBuf should be. The function will return false if the image does not +// fit inside outBuf. +extern "C" int ConvertCRNtoRGBA(const void *buffer, int length, int outBufLen, void* outBuf) { + crnd::crn_texture_info ti; + if(!crnd::crnd_get_texture_info(buffer, length, &ti) || + // Ensure we are not trying to load a cubemap (which has 6 faces...) + (ti.m_faces != 1) ) { + return false; + } + + // Sanity check mipmaps. + if (ti.m_levels <= 0) { + return false; + } + + // The largest layer is always layer 0, so load that one. + crnd::crn_level_info li; + if (!crnd::crnd_get_level_info( buffer, length, 0, &li)) { + return false; + } + + // Ensure we can fit the final image in outBuf. + if (outBufLen < ti.m_width * ti.m_height) { + return false; + } + + crnd::crnd_unpack_context ctx = crnd::crnd_unpack_begin(buffer, length); + if (!ctx) { + return false; + } + + // Since the texture is compressed and the crunch library doesn't provide the code to convert the code + // to RGBAImage, we'll need to convert it to DDS first and use the DDS decompression routines to get + // the raw pixels (theoretically, we could refactor the DDS functions to be generalized, but for now, + // this seems much more maintainable...). This code is cribbed from the example code in + // the crunch repo: https://github.com/DaemonEngine/crunch/blob/master/example2/example2.cpp + // Compute the face's width, height, number of DXT blocks per row/col, etc. + // This is not a proper DDS conversion; it's only enough to get the ddslib decompressor to be happy. + const crn_uint32 blocks_x = std::max(1U, (ti.m_width + 3) >> 2); + const crn_uint32 blocks_y = std::max(1U, (ti.m_height + 3) >> 2); + const crn_uint32 row_pitch = blocks_x * crnd::crnd_get_bytes_per_dxt_block(ti.m_format); + const crn_uint32 total_face_size = row_pitch * blocks_y; + const crn_uint32 ddsSize = sizeof(ddsBuffer_t) + total_face_size; + std::unique_ptr ddsBuffer(new char[ddsSize]); + memset(ddsBuffer.get(), 0, ddsSize); + + + ddsBuffer_t* dds = reinterpret_cast(ddsBuffer.get()); + + memcpy(&dds->magic, "DDS ", sizeof(dds->magic)); + dds->size = LittleLong(124); // Size of the DDS header. + dds->height = LittleLong(ti.m_height); + dds->width = LittleLong(ti.m_width); + dds->mipMapCount = LittleLong(1); + + dds->pixelFormat.size = LittleLong(sizeof(ddsPixelFormat_t)); + + crn_format fundamental_fmt = crnd::crnd_get_fundamental_dxt_format(ti.m_format); + dds->pixelFormat.fourCC = LittleLong(crnd::crnd_crn_format_to_fourcc(fundamental_fmt)); + if (fundamental_fmt != ti.m_format) { + // It's a funky swizzled DXTn format - write its FOURCC to RGBBitCount. + dds->pixelFormat.rgbBitCount = LittleLong(crnd::crnd_crn_format_to_fourcc(ti.m_format)); + } + char* imageArray[1]; + imageArray[0] = reinterpret_cast(&dds->data); + if (!crnd::crnd_unpack_level(ctx, reinterpret_cast(&imageArray), total_face_size, row_pitch, 0)) { + return false; + } + + if (DDSDecompress(dds, reinterpret_cast(outBuf)) == -1) { + return false; + } + return true; +} diff --git a/libs/crunch/crn_rgba.h b/libs/crunch/crn_rgba.h new file mode 100644 index 00000000..b674957e --- /dev/null +++ b/libs/crunch/crn_rgba.h @@ -0,0 +1,35 @@ +/* + Copyright (C) 2018, Unvanquished Developers + All Rights Reserved. + + This file is part of NetRadiant. + + NetRadiant is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + NetRadiant is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with NetRadiant; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus +// Sets `x` and `y` to the width and height of the input crn image. Returns false if there is an +// error reading the image. +int GetCRNImageSize(const void *buffer, int length, int *x, int *y); + +// Converts a .crn file to RGBA. Stores the pixels in outBuf. Use GetCRNImageSize to get the image +// size to determine how big outBuf should be. The function will return false if the image does not +// fit inside outBuf. +int ConvertCRNtoRGBA(const void *buffer, int length, int outBufLen, void* outBuf); +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/libs/crunch/crnlib.h b/libs/crunch/crnlib.h new file mode 100644 index 00000000..d1da8805 --- /dev/null +++ b/libs/crunch/crnlib.h @@ -0,0 +1,641 @@ +// File: crnlib.h - Advanced DXTn texture compression library. +// Copyright (c) 2010-2016 Richard Geldreich, Jr. and Binomial LLC +// See copyright notice and license at the end of this file. +// +// This header file contains the public crnlib declarations for DXTn, +// clustered DXTn, and CRN compression/decompression. +// +// Note: This library does NOT need to be linked into your game executable if +// all you want to do is transcode .CRN files to raw DXTn bits at run-time. +// The crn_decomp.h header file library contains all the code necessary for +// decompression. +// +// Important: If compiling with gcc, be sure strict aliasing is disabled: -fno-strict-aliasing +#ifndef CRNLIB_H +#define CRNLIB_H + +#ifdef _MSC_VER +#pragma warning(disable : 4127) // conditional expression is constant +#endif + +#define CRNLIB_VERSION 104 + +#define CRNLIB_SUPPORT_ATI_COMPRESS 0 +#define CRNLIB_SUPPORT_SQUISH 0 + +typedef unsigned char crn_uint8; +typedef unsigned short crn_uint16; +typedef unsigned int crn_uint32; +typedef signed char crn_int8; +typedef signed short crn_int16; +typedef signed int crn_int32; +typedef unsigned int crn_bool; + +// crnlib can compress to these file types. +enum crn_file_type { + // .CRN + cCRNFileTypeCRN = 0, + + // .DDS using regular DXT or clustered DXT + cCRNFileTypeDDS, + + cCRNFileTypeForceDWORD = 0xFFFFFFFF +}; + +// Supported compressed pixel formats. +// Basically all the standard DX9 formats, with some swizzled DXT5 formats +// (most of them supported by ATI's Compressonator), along with some ATI/X360 GPU specific formats. +enum crn_format { + cCRNFmtInvalid = -1, + + cCRNFmtDXT1 = 0, + + cCRNFmtFirstValid = cCRNFmtDXT1, + + // cCRNFmtDXT3 is not currently supported when writing to CRN - only DDS. + cCRNFmtDXT3, + + cCRNFmtDXT5, + + // Various DXT5 derivatives + cCRNFmtDXT5_CCxY, // Luma-chroma + cCRNFmtDXT5_xGxR, // Swizzled 2-component + cCRNFmtDXT5_xGBR, // Swizzled 3-component + cCRNFmtDXT5_AGBR, // Swizzled 4-component + + // ATI 3DC and X360 DXN + cCRNFmtDXN_XY, + cCRNFmtDXN_YX, + + // DXT5 alpha blocks only + cCRNFmtDXT5A, + + cCRNFmtETC1, + cCRNFmtETC2, + cCRNFmtETC2A, + + cCRNFmtTotal, + + cCRNFmtForceDWORD = 0xFFFFFFFF +}; + +// Various library/file format limits. +enum crn_limits { + // Max. mipmap level resolution on any axis. + cCRNMaxLevelResolution = 4096, + + cCRNMinPaletteSize = 8, + cCRNMaxPaletteSize = 8192, + + cCRNMaxFaces = 6, + cCRNMaxLevels = 16, + + cCRNMaxHelperThreads = 16, + + cCRNMinQualityLevel = 0, + cCRNMaxQualityLevel = 255 +}; + +// CRN/DDS compression flags. +// See the m_flags member in the crn_comp_params struct, below. +enum crn_comp_flags { + // Enables perceptual colorspace distance metrics if set. + // Important: Be sure to disable this when compressing non-sRGB colorspace images, like normal maps! + // Default: Set + cCRNCompFlagPerceptual = 1, + + // Enables (up to) 8x8 macroblock usage if set. If disabled, only 4x4 blocks are allowed. + // Compression ratio will be lower when disabled, but may cut down on blocky artifacts because the process used to determine + // where large macroblocks can be used without artifacts isn't perfect. + // Default: Set. + cCRNCompFlagHierarchical = 2, + + // cCRNCompFlagQuick disables several output file optimizations - intended for things like quicker previews. + // Default: Not set. + cCRNCompFlagQuick = 4, + + // DXT1: OK to use DXT1 alpha blocks for better quality or DXT1A transparency. + // DXT5: OK to use both DXT5 block types. + // Currently only used when writing to .DDS files, as .CRN uses only a subset of the possible DXTn block types. + // Default: Set. + cCRNCompFlagUseBothBlockTypes = 8, + + // OK to use DXT1A transparent indices to encode black (assumes pixel shader ignores fetched alpha). + // Currently only used when writing to .DDS files, .CRN never uses alpha blocks. + // Default: Not set. + cCRNCompFlagUseTransparentIndicesForBlack = 16, + + // Disables endpoint caching, for more deterministic output. + // Currently only used when writing to .DDS files. + // Default: Not set. + cCRNCompFlagDisableEndpointCaching = 32, + + // If enabled, use the cCRNColorEndpointPaletteSize, etc. params to control the CRN palette sizes. Only useful when writing to .CRN files. + // Default: Not set. + cCRNCompFlagManualPaletteSizes = 64, + + // If enabled, DXT1A alpha blocks are used to encode single bit transparency. + // Default: Not set. + cCRNCompFlagDXT1AForTransparency = 128, + + // If enabled, the DXT1 compressor's color distance metric assumes the pixel shader will be converting the fetched RGB results to luma (Y part of YCbCr). + // This increases quality when compressing grayscale images, because the compressor can spread the luma error amoung all three channels (i.e. it can generate blocks + // with some chroma present if doing so will ultimately lead to lower luma error). + // Only enable on grayscale source images. + // Default: Not set. + cCRNCompFlagGrayscaleSampling = 256, + + // If enabled, debug information will be output during compression. + // Default: Not set. + cCRNCompFlagDebugging = 0x80000000, + + cCRNCompFlagForceDWORD = 0xFFFFFFFF +}; + +// Controls DXTn quality vs. speed control - only used when compressing to .DDS. +enum crn_dxt_quality { + cCRNDXTQualitySuperFast, + cCRNDXTQualityFast, + cCRNDXTQualityNormal, + cCRNDXTQualityBetter, + cCRNDXTQualityUber, + + cCRNDXTQualityTotal, + + cCRNDXTQualityForceDWORD = 0xFFFFFFFF +}; + +// Which DXTn compressor to use when compressing to plain (non-clustered) .DDS. +enum crn_dxt_compressor_type { + cCRNDXTCompressorCRN, // Use crnlib's ETC1 or DXTc block compressor (default, highest quality, comparable or better than ati_compress or squish, and crnlib's ETC1 is a lot fasterw with similiar quality to Erricson's) + cCRNDXTCompressorCRNF, // Use crnlib's "fast" DXTc block compressor + cCRNDXTCompressorRYG, // Use RYG's DXTc block compressor (low quality, but very fast) + +#if CRNLIB_SUPPORT_ATI_COMPRESS + cCRNDXTCompressorATI, +#endif + +#if CRNLIB_SUPPORT_SQUISH + cCRNDXTCompressorSquish, +#endif + + cCRNTotalDXTCompressors, + + cCRNDXTCompressorForceDWORD = 0xFFFFFFFF +}; + +// Progress callback function. +// Processing will stop prematurely (and fail) if the callback returns false. +// phase_index, total_phases - high level progress +// subphase_index, total_subphases - progress within current phase +typedef crn_bool (*crn_progress_callback_func)(crn_uint32 phase_index, crn_uint32 total_phases, crn_uint32 subphase_index, crn_uint32 total_subphases, void* pUser_data_ptr); + +// CRN/DDS compression parameters struct. +struct crn_comp_params { + inline crn_comp_params() { clear(); } + + // Clear struct to default parameters. + inline void clear() { + m_size_of_obj = sizeof(*this); + m_file_type = cCRNFileTypeCRN; + m_faces = 1; + m_width = 0; + m_height = 0; + m_levels = 1; + m_format = cCRNFmtDXT1; + m_flags = cCRNCompFlagPerceptual | cCRNCompFlagHierarchical | cCRNCompFlagUseBothBlockTypes; + + for (crn_uint32 f = 0; f < cCRNMaxFaces; f++) + for (crn_uint32 l = 0; l < cCRNMaxLevels; l++) + m_pImages[f][l] = NULL; + + m_target_bitrate = 0.0f; + m_quality_level = cCRNMaxQualityLevel; + m_dxt1a_alpha_threshold = 128; + m_dxt_quality = cCRNDXTQualityUber; + m_dxt_compressor_type = cCRNDXTCompressorCRN; + m_alpha_component = 3; + + m_crn_adaptive_tile_color_psnr_derating = 2.0f; + m_crn_adaptive_tile_alpha_psnr_derating = 2.0f; + m_crn_color_endpoint_palette_size = 0; + m_crn_color_selector_palette_size = 0; + m_crn_alpha_endpoint_palette_size = 0; + m_crn_alpha_selector_palette_size = 0; + + m_num_helper_threads = 0; + m_userdata0 = 0; + m_userdata1 = 0; + m_pProgress_func = NULL; + m_pProgress_func_data = NULL; + } + + inline bool operator==(const crn_comp_params& rhs) const { +#define CRNLIB_COMP(x) \ + do { \ + if ((x) != (rhs.x)) \ + return false; \ + } while (0) + CRNLIB_COMP(m_size_of_obj); + CRNLIB_COMP(m_file_type); + CRNLIB_COMP(m_faces); + CRNLIB_COMP(m_width); + CRNLIB_COMP(m_height); + CRNLIB_COMP(m_levels); + CRNLIB_COMP(m_format); + CRNLIB_COMP(m_flags); + CRNLIB_COMP(m_target_bitrate); + CRNLIB_COMP(m_quality_level); + CRNLIB_COMP(m_dxt1a_alpha_threshold); + CRNLIB_COMP(m_dxt_quality); + CRNLIB_COMP(m_dxt_compressor_type); + CRNLIB_COMP(m_alpha_component); + CRNLIB_COMP(m_crn_adaptive_tile_color_psnr_derating); + CRNLIB_COMP(m_crn_adaptive_tile_alpha_psnr_derating); + CRNLIB_COMP(m_crn_color_endpoint_palette_size); + CRNLIB_COMP(m_crn_color_selector_palette_size); + CRNLIB_COMP(m_crn_alpha_endpoint_palette_size); + CRNLIB_COMP(m_crn_alpha_selector_palette_size); + CRNLIB_COMP(m_num_helper_threads); + CRNLIB_COMP(m_userdata0); + CRNLIB_COMP(m_userdata1); + CRNLIB_COMP(m_pProgress_func); + CRNLIB_COMP(m_pProgress_func_data); + + for (crn_uint32 f = 0; f < cCRNMaxFaces; f++) + for (crn_uint32 l = 0; l < cCRNMaxLevels; l++) + CRNLIB_COMP(m_pImages[f][l]); + +#undef CRNLIB_COMP + return true; + } + + // Returns true if the input parameters are reasonable. + inline bool check() const { + if ((m_file_type > cCRNFileTypeDDS) || + (((int)m_quality_level < (int)cCRNMinQualityLevel) || ((int)m_quality_level > (int)cCRNMaxQualityLevel)) || + (m_dxt1a_alpha_threshold > 255) || + ((m_faces != 1) && (m_faces != 6)) || + ((m_width < 1) || (m_width > cCRNMaxLevelResolution)) || + ((m_height < 1) || (m_height > cCRNMaxLevelResolution)) || + ((m_levels < 1) || (m_levels > cCRNMaxLevels)) || + ((m_format < cCRNFmtDXT1) || (m_format >= cCRNFmtTotal)) || + ((m_crn_color_endpoint_palette_size) && ((m_crn_color_endpoint_palette_size < cCRNMinPaletteSize) || (m_crn_color_endpoint_palette_size > cCRNMaxPaletteSize))) || + ((m_crn_color_selector_palette_size) && ((m_crn_color_selector_palette_size < cCRNMinPaletteSize) || (m_crn_color_selector_palette_size > cCRNMaxPaletteSize))) || + ((m_crn_alpha_endpoint_palette_size) && ((m_crn_alpha_endpoint_palette_size < cCRNMinPaletteSize) || (m_crn_alpha_endpoint_palette_size > cCRNMaxPaletteSize))) || + ((m_crn_alpha_selector_palette_size) && ((m_crn_alpha_selector_palette_size < cCRNMinPaletteSize) || (m_crn_alpha_selector_palette_size > cCRNMaxPaletteSize))) || + (m_alpha_component > 3) || + (m_num_helper_threads > cCRNMaxHelperThreads) || + (m_dxt_quality > cCRNDXTQualityUber) || + (m_dxt_compressor_type >= cCRNTotalDXTCompressors)) { + return false; + } + return true; + } + + // Helper to set/get flags from m_flags member. + inline bool get_flag(crn_comp_flags flag) const { return (m_flags & flag) != 0; } + inline void set_flag(crn_comp_flags flag, bool val) { + m_flags &= ~flag; + if (val) + m_flags |= flag; + } + + crn_uint32 m_size_of_obj; + + crn_file_type m_file_type; // Output file type: cCRNFileTypeCRN or cCRNFileTypeDDS. + + crn_uint32 m_faces; // 1 (2D map) or 6 (cubemap) + crn_uint32 m_width; // [1,cCRNMaxLevelResolution], non-power of 2 OK, non-square OK + crn_uint32 m_height; // [1,cCRNMaxLevelResolution], non-power of 2 OK, non-square OK + crn_uint32 m_levels; // [1,cCRNMaxLevelResolution], non-power of 2 OK, non-square OK + + crn_format m_format; // Output pixel format. + + crn_uint32 m_flags; // see crn_comp_flags enum + + // Array of pointers to 32bpp input images. + const crn_uint32* m_pImages[cCRNMaxFaces][cCRNMaxLevels]; + + // Target bitrate - if non-zero, the compressor will use an interpolative search to find the + // highest quality level that is <= the target bitrate. If it fails to find a bitrate high enough, it'll + // try disabling adaptive block sizes (cCRNCompFlagHierarchical flag) and redo the search. This process can be pretty slow. + float m_target_bitrate; + + // Desired quality level. + // Currently, CRN and DDS quality levels are not compatible with eachother from an image quality standpoint. + crn_uint32 m_quality_level; // [cCRNMinQualityLevel, cCRNMaxQualityLevel] + + // DXTn compression parameters. + crn_uint32 m_dxt1a_alpha_threshold; + crn_dxt_quality m_dxt_quality; + crn_dxt_compressor_type m_dxt_compressor_type; + + // Alpha channel's component. Defaults to 3. + crn_uint32 m_alpha_component; + + // Various low-level CRN specific parameters. + float m_crn_adaptive_tile_color_psnr_derating; + float m_crn_adaptive_tile_alpha_psnr_derating; + + crn_uint32 m_crn_color_endpoint_palette_size; // [cCRNMinPaletteSize,cCRNMaxPaletteSize] + crn_uint32 m_crn_color_selector_palette_size; // [cCRNMinPaletteSize,cCRNMaxPaletteSize] + + crn_uint32 m_crn_alpha_endpoint_palette_size; // [cCRNMinPaletteSize,cCRNMaxPaletteSize] + crn_uint32 m_crn_alpha_selector_palette_size; // [cCRNMinPaletteSize,cCRNMaxPaletteSize] + + // Number of helper threads to create during compression. 0=no threading. + crn_uint32 m_num_helper_threads; + + // CRN userdata0 and userdata1 members, which are written directly to the header of the output file. + crn_uint32 m_userdata0; + crn_uint32 m_userdata1; + + // User provided progress callback. + crn_progress_callback_func m_pProgress_func; + void* m_pProgress_func_data; +}; + +// Mipmap generator's mode. +enum crn_mip_mode { + cCRNMipModeUseSourceOrGenerateMips, // Use source texture's mipmaps if it has any, otherwise generate new mipmaps + cCRNMipModeUseSourceMips, // Use source texture's mipmaps if it has any, otherwise the output has no mipmaps + cCRNMipModeGenerateMips, // Always generate new mipmaps + cCRNMipModeNoMips, // Output texture has no mipmaps + + cCRNMipModeTotal, + + cCRNModeForceDWORD = 0xFFFFFFFF +}; + +const char* crn_get_mip_mode_desc(crn_mip_mode m); +const char* crn_get_mip_mode_name(crn_mip_mode m); + +// Mipmap generator's filter kernel. +enum crn_mip_filter { + cCRNMipFilterBox, + cCRNMipFilterTent, + cCRNMipFilterLanczos4, + cCRNMipFilterMitchell, + cCRNMipFilterKaiser, // Kaiser=default mipmap filter + + cCRNMipFilterTotal, + + cCRNMipFilterForceDWORD = 0xFFFFFFFF +}; + +const char* crn_get_mip_filter_name(crn_mip_filter f); + +// Mipmap generator's scale mode. +enum crn_scale_mode { + cCRNSMDisabled, + cCRNSMAbsolute, + cCRNSMRelative, + cCRNSMLowerPow2, + cCRNSMNearestPow2, + cCRNSMNextPow2, + + cCRNSMTotal, + + cCRNSMForceDWORD = 0xFFFFFFFF +}; + +const char* crn_get_scale_mode_desc(crn_scale_mode sm); + +// Mipmap generator parameters. +struct crn_mipmap_params { + inline crn_mipmap_params() { clear(); } + + inline void clear() { + m_size_of_obj = sizeof(*this); + m_mode = cCRNMipModeUseSourceOrGenerateMips; + m_filter = cCRNMipFilterKaiser; + m_gamma_filtering = true; + m_gamma = 2.2f; + // Default "blurriness" factor of .9 actually sharpens the output a little. + m_blurriness = .9f; + m_renormalize = false; + m_rtopmip = false; + m_tiled = false; + m_max_levels = cCRNMaxLevels; + m_min_mip_size = 1; + + m_scale_mode = cCRNSMDisabled; + m_scale_x = 1.0f; + m_scale_y = 1.0f; + + m_window_left = 0; + m_window_top = 0; + m_window_right = 0; + m_window_bottom = 0; + + m_clamp_scale = false; + m_clamp_width = 0; + m_clamp_height = 0; + } + + inline bool check() const { return true; } + + inline bool operator==(const crn_mipmap_params& rhs) const { +#define CRNLIB_COMP(x) \ + do { \ + if ((x) != (rhs.x)) \ + return false; \ + } while (0) + CRNLIB_COMP(m_size_of_obj); + CRNLIB_COMP(m_mode); + CRNLIB_COMP(m_filter); + CRNLIB_COMP(m_gamma_filtering); + CRNLIB_COMP(m_gamma); + CRNLIB_COMP(m_blurriness); + CRNLIB_COMP(m_renormalize); + CRNLIB_COMP(m_rtopmip); + CRNLIB_COMP(m_tiled); + CRNLIB_COMP(m_max_levels); + CRNLIB_COMP(m_min_mip_size); + CRNLIB_COMP(m_scale_mode); + CRNLIB_COMP(m_scale_x); + CRNLIB_COMP(m_scale_y); + CRNLIB_COMP(m_window_left); + CRNLIB_COMP(m_window_top); + CRNLIB_COMP(m_window_right); + CRNLIB_COMP(m_window_bottom); + CRNLIB_COMP(m_clamp_scale); + CRNLIB_COMP(m_clamp_width); + CRNLIB_COMP(m_clamp_height); + return true; +#undef CRNLIB_COMP + } + crn_uint32 m_size_of_obj; + + crn_mip_mode m_mode; + crn_mip_filter m_filter; + + crn_bool m_gamma_filtering; + float m_gamma; + + float m_blurriness; + + crn_uint32 m_max_levels; + crn_uint32 m_min_mip_size; + + crn_bool m_renormalize; + crn_bool m_rtopmip; + crn_bool m_tiled; + + crn_scale_mode m_scale_mode; + float m_scale_x; + float m_scale_y; + + crn_uint32 m_window_left; + crn_uint32 m_window_top; + crn_uint32 m_window_right; + crn_uint32 m_window_bottom; + + crn_bool m_clamp_scale; + crn_uint32 m_clamp_width; + crn_uint32 m_clamp_height; +}; + +// -------- High-level helper function definitions for CDN/DDS compression. + +#ifndef CRNLIB_MIN_ALLOC_ALIGNMENT +#define CRNLIB_MIN_ALLOC_ALIGNMENT sizeof(size_t) * 2 +#endif + +// Function to set an optional user provided memory allocation/reallocation/msize routines. +// By default, crnlib just uses malloc(), free(), etc. for all allocations. +typedef void* (*crn_realloc_func)(void* p, size_t size, size_t* pActual_size, bool movable, void* pUser_data); +typedef size_t (*crn_msize_func)(void* p, void* pUser_data); +void crn_set_memory_callbacks(crn_realloc_func pRealloc, crn_msize_func pMSize, void* pUser_data); + +// Frees memory blocks allocated by crn_compress(), crn_decompress_crn_to_dds(), or crn_decompress_dds_to_images(). +void crn_free_block(void* pBlock); + +// Compresses a 32-bit/pixel texture to either: a regular DX9 DDS file, a "clustered" (or reduced entropy) DX9 DDS file, or a CRN file in memory. +// Input parameters: +// comp_params is the compression parameters struct, defined above. +// compressed_size will be set to the size of the returned memory block containing the output file. +// The returned block must be freed by calling crn_free_block(). +// *pActual_quality_level will be set to the actual quality level used to compress the image. May be NULL. +// *pActual_bitrate will be set to the output file's effective bitrate, possibly taking into account LZMA compression. May be NULL. +// Return value: +// The compressed file data, or NULL on failure. +// compressed_size will be set to the size of the returned memory buffer. +// Notes: +// A "regular" DDS file is compressed using normal DXTn compression at the specified DXT quality level. +// A "clustered" DDS file is compressed using clustered DXTn compression to either the target bitrate or the specified integer quality factor. +// The output file is a standard DX9 format DDS file, except the compressor assumes you will be later losslessly compressing the DDS output file using the LZMA algorithm. +// A texture is defined as an array of 1 or 6 "faces" (6 faces=cubemap), where each "face" consists of between [1,cCRNMaxLevels] mipmap levels. +// Mipmap levels are simple 32-bit 2D images with a pitch of width*sizeof(uint32), arranged in the usual raster order (top scanline first). +// The image pixels may be grayscale (YYYX bytes in memory), grayscale/alpha (YYYA in memory), 24-bit (RGBX in memory), or 32-bit (RGBA) colors (where "X"=don't care). +// RGB color data is generally assumed to be in the sRGB colorspace. If not, be sure to clear the "cCRNCompFlagPerceptual" in the crn_comp_params struct! +void* crn_compress(const crn_comp_params& comp_params, crn_uint32& compressed_size, crn_uint32* pActual_quality_level = NULL, float* pActual_bitrate = NULL); + +// Like the above function, except this function can also do things like generate mipmaps, and resize or crop the input texture before compression. +// The actual operations performed are controlled by the crn_mipmap_params struct members. +// Be sure to set the "m_gamma_filtering" member of crn_mipmap_params to false if the input texture is not sRGB. +void* crn_compress(const crn_comp_params& comp_params, const crn_mipmap_params& mip_params, crn_uint32& compressed_size, crn_uint32* pActual_quality_level = NULL, float* pActual_bitrate = NULL); + +// Transcodes an entire CRN file to DDS using the crn_decomp.h header file library to do most of the heavy lifting. +// The output DDS file's format is guaranteed to be one of the DXTn formats in the crn_format enum. +// This is a fast operation, because the CRN format is explicitly designed to be efficiently transcodable to DXTn. +// For more control over decompression, see the lower-level helper functions in crn_decomp.h, which do not depend at all on crnlib. +void* crn_decompress_crn_to_dds(const void* pCRN_file_data, crn_uint32& file_size); + +// Decompresses an entire DDS file in any supported format to uncompressed 32-bit/pixel image(s). +// See the crnlib::pixel_format enum in inc/dds_defs.h for a list of the supported DDS formats. +// You are responsible for freeing each image block, either by calling crn_free_all_images() or manually calling crn_free_block() on each image pointer. +struct crn_texture_desc { + crn_uint32 m_faces; + crn_uint32 m_width; + crn_uint32 m_height; + crn_uint32 m_levels; + crn_uint32 m_fmt_fourcc; // Same as crnlib::pixel_format +}; +bool crn_decompress_dds_to_images(const void* pDDS_file_data, crn_uint32 dds_file_size, crn_uint32** ppImages, crn_texture_desc& tex_desc); + +// Frees all images allocated by crn_decompress_dds_to_images(). +void crn_free_all_images(crn_uint32** ppImages, const crn_texture_desc& desc); + +// -------- crn_format related helpers functions. + +// Returns the FOURCC format equivalent to the specified crn_format. +crn_uint32 crn_get_format_fourcc(crn_format fmt); + +// Returns the crn_format's bits per texel. +crn_uint32 crn_get_format_bits_per_texel(crn_format fmt); + +// Returns the crn_format's number of bytes per block. +crn_uint32 crn_get_bytes_per_dxt_block(crn_format fmt); + +// Returns the non-swizzled, basic DXTn version of the specified crn_format. +// This is the format you would supply D3D or OpenGL. +crn_format crn_get_fundamental_dxt_format(crn_format fmt); + +// -------- String helpers. + +// Converts a crn_file_type to a string. +const char* crn_get_file_type_ext(crn_file_type file_type); + +// Converts a crn_format to a string. +const char* crn_get_format_string(crn_format fmt); + +// Converts a crn_dxt_quality to a string. +const char* crn_get_dxt_quality_string(crn_dxt_quality q); + +// -------- Low-level DXTn 4x4 block compressor API + +// crnlib's DXTn endpoint optimizer actually supports any number of source pixels (i.e. from 1 to thousands, not just 16), +// but for simplicity this API only supports 4x4 texel blocks. +typedef void* crn_block_compressor_context_t; + +// Create a DXTn block compressor. +// This function only supports the basic/nonswizzled "fundamental" formats: DXT1, DXT3, DXT5, DXT5A, DXN_XY and DXN_YX. +// Avoid calling this multiple times if you intend on compressing many blocks, because it allocates some memory. +crn_block_compressor_context_t crn_create_block_compressor(const crn_comp_params& params); + +// Compresses a block of 16 pixels to the destination DXTn block. +// pDst_block should be 8 (for DXT1/DXT5A) or 16 bytes (all the others). +// pPixels should be an array of 16 crn_uint32's. Each crn_uint32 must be r,g,b,a (r is always first) in memory. +void crn_compress_block(crn_block_compressor_context_t pContext, const crn_uint32* pPixels, void* pDst_block); + +// Frees a DXTn block compressor. +void crn_free_block_compressor(crn_block_compressor_context_t pContext); + +// Unpacks a compressed block to pDst_pixels. +// pSrc_block should be 8 (for DXT1/DXT5A) or 16 bytes (all the others). +// pDst_pixel should be an array of 16 crn_uint32's. Each uint32 will be r,g,b,a (r is always first) in memory. +// crn_fmt should be one of the "fundamental" formats: DXT1, DXT3, DXT5, DXT5A, DXN_XY and DXN_YX. +// The various swizzled DXT5 formats (such as cCRNFmtDXT5_xGBR, etc.) will be unpacked as if they where plain DXT5. +// Returns false if the crn_fmt is invalid. +bool crn_decompress_block(const void* pSrc_block, crn_uint32* pDst_pixels, crn_format crn_fmt); + +#endif // CRNLIB_H + +//------------------------------------------------------------------------------ +// +// crnlib uses the ZLIB license: +// http://opensource.org/licenses/Zlib +// +// Copyright (c) 2010-2016 Richard Geldreich, Jr. and Binomial LLC +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//------------------------------------------------------------------------------ diff --git a/plugins/image/CMakeLists.txt b/plugins/image/CMakeLists.txt index e1f40cc5..f0b021cc 100644 --- a/plugins/image/CMakeLists.txt +++ b/plugins/image/CMakeLists.txt @@ -6,8 +6,10 @@ radiant_plugin(image ktx.cpp ktx.h pcx.cpp pcx.h tga.cpp tga.h + crn.cpp crn.h ) find_package(JPEG REQUIRED) -target_include_directories(image PRIVATE ${JPEG_INCLUDE_DIR}) -target_link_libraries(image PRIVATE ddslib etclib ${JPEG_LIBRARIES}) +target_include_directories(image PRIVATE ${JPEG_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/libs/crunch) +target_link_libraries(image PRIVATE ddslib etclib crnlib ${JPEG_LIBRARIES}) +target_compile_options(image PRIVATE -fexceptions) diff --git a/plugins/image/crn.cpp b/plugins/image/crn.cpp new file mode 100644 index 00000000..85e1e5cb --- /dev/null +++ b/plugins/image/crn.cpp @@ -0,0 +1,55 @@ +/* + Copyright (C) 2018, Unvanquished Developers + All Rights Reserved. + + This file is part of NetRadiant. + + NetRadiant is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + NetRadiant is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with NetRadiant; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include "crn.h" + +#include + +#include "ifilesystem.h" +#include "iarchive.h" +#include "idatastream.h" + +#include "crn_rgba.h" +#include "ddslib.h" +#include "imagelib.h" + +Image *LoadCRNBuff(const byte *buffer, int length) +{ + int width, height; + if (!GetCRNImageSize(buffer, length, &width, &height)) { + globalErrorStream() << "ERROR: Error getting crn imag dimensions.\n"; + return nullptr; + } + RGBAImage *image = new RGBAImage(width, height); + if (!ConvertCRNtoRGBA(buffer, length, width * height, image->getRGBAPixels())) { + globalErrorStream() << "ERROR: Error decoding crn image.\n"; + image->release(); + return nullptr; + } + return image; +} + +Image *LoadCRN(ArchiveFile &file) +{ + ScopedArchiveBuffer buffer(file); + return LoadCRNBuff(buffer.buffer, buffer.length); +} diff --git a/plugins/image/crn.h b/plugins/image/crn.h new file mode 100644 index 00000000..4e97556e --- /dev/null +++ b/plugins/image/crn.h @@ -0,0 +1,32 @@ +/* + Copyright (C) 2018, Unvanquished Developers + All Rights Reserved. + + This file is part of NetRadiant. + + NetRadiant is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + NetRadiant is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with NetRadiant; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#if !defined( INCLUDED_CRN_H ) +#define INCLUDED_CRN_H + +class Image; + +class ArchiveFile; + +Image *LoadCRN(ArchiveFile &file); + +#endif + diff --git a/plugins/image/image.cpp b/plugins/image/image.cpp index f7f73522..574e4fab 100644 --- a/plugins/image/image.cpp +++ b/plugins/image/image.cpp @@ -28,6 +28,7 @@ #include "pcx.h" #include "dds.h" #include "ktx.h" +#include "crn.h" #include "modulesystem/singletonmodule.h" @@ -173,6 +174,29 @@ typedef SingletonModule ImageKTXModule; ImageKTXModule g_ImageKTXModule; +class ImageCRNAPI { + _QERPlugImageTable m_imagecrn; +public: + typedef _QERPlugImageTable Type; + + STRING_CONSTANT(Name, "crn"); + + ImageCRNAPI() + { + m_imagecrn.loadImage = LoadCRN; + } + + _QERPlugImageTable *getTable() + { + return &m_imagecrn; + } +}; + +typedef SingletonModule ImageCRNModule; + +ImageCRNModule g_ImageCRNModule; + + extern "C" void RADIANT_DLLEXPORT Radiant_RegisterModules(ModuleServer &server) { initialiseModule(server); @@ -183,4 +207,5 @@ extern "C" void RADIANT_DLLEXPORT Radiant_RegisterModules(ModuleServer &server) g_ImagePCXModule.selfRegister(); g_ImageDDSModule.selfRegister(); g_ImageKTXModule.selfRegister(); + g_ImageCRNModule.selfRegister(); } -- 2.39.2