merging v2.0 from xuastc_private

This commit is contained in:
Richard Geldreich
2026-01-19 01:59:54 -05:00
parent ea6778b2b5
commit e93d3beb36
49 changed files with 72432 additions and 26002 deletions

View File

@@ -1,5 +1,5 @@
// basisu.h
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2026 Binomial LLC. All Rights Reserved.
// Important: If compiling with gcc, be sure strict aliasing is disabled: -fno-strict-aliasing
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,12 +15,16 @@
// limitations under the License.
#pragma once
#ifndef BASISD_SUPPORT_XUASTC
#define BASISD_SUPPORT_XUASTC 1
#endif
#ifdef _MSC_VER
#pragma warning (disable : 4201)
#pragma warning (disable : 4127) // warning C4127: conditional expression is constant
#pragma warning (disable : 4530) // C++ exception handler used, but unwind semantics are not enabled.
#endif // _MSC_VER
#include <stdlib.h>
@@ -40,9 +44,11 @@
#include <assert.h>
#include <random>
#include <inttypes.h>
#include <cfloat>
#include "basisu_containers.h"
// We never use min/max macros, slam them to off.
#ifdef max
#undef max
#endif
@@ -57,6 +63,7 @@
// Set to one to enable debug printf()'s when any errors occur, for development/debugging. Especially useful for WebGL development.
#ifndef BASISU_FORCE_DEVEL_MESSAGES
// Do not check in as 1!
#define BASISU_FORCE_DEVEL_MESSAGES 0
#endif
@@ -93,6 +100,7 @@ namespace basisu
typedef basisu::vector<int> int_vec;
typedef basisu::vector<bool> bool_vec;
typedef basisu::vector<float> float_vec;
typedef basisu::vector<double> double_vec;
void enable_debug_printf(bool enabled);
void debug_printf(const char *pFmt, ...);
@@ -109,14 +117,14 @@ namespace basisu
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#endif
template <typename T> inline void clear_obj(T& obj) { memset((void *)&obj, 0, sizeof(obj)); }
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif
#endif
constexpr double cPiD = 3.14159265358979323846264338327950288;
constexpr float REALLY_SMALL_FLOAT_VAL = .000000125f;
@@ -124,7 +132,7 @@ namespace basisu
constexpr float BIG_FLOAT_VAL = 1e+30f;
template <typename T0, typename T1> inline T0 lerp(T0 a, T0 b, T1 c) { return a + (b - a) * c; }
inline float clampf(float value, float low, float high) { if (value < low) value = low; else if (value > high) value = high; return value; }
inline float saturate(float value) { return clampf(value, 0, 1.0f); }
inline uint8_t minimumub(uint8_t a, uint8_t b) { return (a < b) ? a : b; }
@@ -141,6 +149,41 @@ namespace basisu
template<typename T> inline T square(T a) { return a * a; }
template<typename T> inline T sign(T a) { return (a < 0) ? (T)-1 : ((a == 0) ? (T)0 : (T)1); }
inline int imod(int i, int d)
{
assert(i != INT_MIN);
if (i >= 0)
return i % d;
int r = (-i) % d;
return (r == 0) ? 0 : d - r;
}
inline uint8_t safe_cast_uint8(uint32_t x)
{
assert(x <= UINT8_MAX);
return (uint8_t)x;
}
inline int8_t safe_cast_int8(int32_t x)
{
assert((x >= INT8_MIN) && (x <= INT8_MAX));
return (int8_t)x;
}
inline uint16_t safe_cast_uint16(uint32_t x)
{
assert(x <= UINT16_MAX);
return (uint16_t)x;
}
inline int16_t safe_cast_int16(int32_t x)
{
assert((x >= INT16_MIN) && (x <= INT16_MAX));
return (int16_t)x;
}
inline bool equal_tol(float a, float b, float t) { return fabsf(a - b) <= ((maximum(fabsf(a), fabsf(b)) + 1.0f) * t); }
inline bool equal_tol(double a, double b, double t) { return fabs(a - b) <= ((maximum(fabs(a), fabs(b)) + 1.0f) * t); }
@@ -161,27 +204,35 @@ namespace basisu
temp = 0;
return temp;
}
inline uint32_t iabs(int32_t i) { return (i < 0) ? static_cast<uint32_t>(-i) : static_cast<uint32_t>(i); }
inline uint64_t iabs64(int64_t i) { return (i < 0) ? static_cast<uint64_t>(-i) : static_cast<uint64_t>(i); }
template<typename T> inline void clear_vector(T &vec) { vec.erase(vec.begin(), vec.end()); }
template<typename T> inline void clear_vector(T &vec) { vec.erase(vec.begin(), vec.end()); }
template<typename T> inline typename T::value_type *enlarge_vector(T &vec, size_t n) { size_t cs = vec.size(); vec.resize(cs + n); return &vec[cs]; }
inline bool is_pow2(uint32_t x) { return x && ((x & (x - 1U)) == 0U); }
inline bool is_pow2(uint64_t x) { return x && ((x & (x - 1U)) == 0U); }
template<typename T> inline T range_check(T v, T minv, T maxv) { assert(v >= minv && v <= maxv); BASISU_NOTE_UNUSED(minv); BASISU_NOTE_UNUSED(maxv); return v; }
template<typename T> inline T range_check(T v, T maxv) { assert(v <= maxv); BASISU_NOTE_UNUSED(maxv); return v; }
template<typename T> inline T open_range_check(T v, T minv, T maxv) { assert(v >= minv && v < maxv); BASISU_NOTE_UNUSED(minv); BASISU_NOTE_UNUSED(maxv); return v; }
template<typename T> inline T open_range_check(T v, T maxv) { assert(v < maxv); BASISU_NOTE_UNUSED(maxv); return v; }
// Open interval
inline bool in_bounds(int v, int l, int h)
inline bool is_in_bounds(int v, int l, int h)
{
return (v >= l) && (v < h);
}
// Closed interval
inline bool in_range(int v, int l, int h)
inline bool is_in_range(int v, int l, int h)
{
return (v >= l) && (v <= h);
}
inline bool is_in_range(float v, float l, float h)
{
return (v >= l) && (v <= h);
}
@@ -192,7 +243,7 @@ namespace basisu
inline uint32_t get_bit(uint32_t src, int ndx)
{
assert(in_bounds(ndx, 0, 32));
assert(is_in_bounds(ndx, 0, 32));
return (src >> ndx) & 1;
}
@@ -204,7 +255,7 @@ namespace basisu
inline uint32_t get_bits(uint32_t val, int low, int high)
{
const int num_bits = (high - low) + 1;
assert(in_range(num_bits, 1, 32));
assert(is_in_range(num_bits, 1, 32));
val >>= low;
if (num_bits != 32)
@@ -213,8 +264,8 @@ namespace basisu
return val;
}
template<typename T, typename R> inline void append_vector(T &vec, const R *pObjs, size_t n)
{
template<typename T, typename R> inline void append_vector(T &vec, const R *pObjs, size_t n)
{
if (n)
{
if (vec.size())
@@ -265,7 +316,7 @@ namespace basisu
for (size_t i = 0; i < vec.size(); i++)
vec[i] = obj;
}
inline uint64_t read_be64(const void *p)
{
uint64_t val = 0;
@@ -307,6 +358,14 @@ namespace basisu
return (m != 0) ? (y - m) : m;
}
inline float posmodf(float x, float y)
{
float m = fmodf(x, y);
if (m < 0.0f)
m += y;
return m;
}
inline bool do_excl_ranges_overlap(int la, int ha, int lb, int hb)
{
assert(la < ha && lb < hb);
@@ -331,7 +390,7 @@ namespace basisu
pBytes[2] = (uint8_t)(val >> 16U);
pBytes[3] = (uint8_t)(val >> 24U);
}
// Always little endian 1-8 byte unsigned int
template<uint32_t NumBytes>
struct packed_uint
@@ -341,21 +400,21 @@ namespace basisu
inline packed_uint() { static_assert(NumBytes <= sizeof(uint64_t), "Invalid NumBytes"); }
inline packed_uint(uint64_t v) { *this = v; }
inline packed_uint(const packed_uint& other) { *this = other; }
inline packed_uint& operator= (uint64_t v)
{
inline packed_uint& operator= (uint64_t v)
{
// TODO: Add assert on truncation?
for (uint32_t i = 0; i < NumBytes; i++)
m_bytes[i] = static_cast<uint8_t>(v >> (i * 8));
return *this;
for (uint32_t i = 0; i < NumBytes; i++)
m_bytes[i] = static_cast<uint8_t>(v >> (i * 8));
return *this;
}
inline packed_uint& operator= (const packed_uint& rhs)
{
memcpy(m_bytes, rhs.m_bytes, sizeof(m_bytes));
inline packed_uint& operator= (const packed_uint& rhs)
{
memcpy(m_bytes, rhs.m_bytes, sizeof(m_bytes));
return *this;
}
inline uint64_t get_uint64() const
{
// Some compilers may warn about this code. It clearly cannot access beyond the end of the m_bytes struct here.
@@ -411,7 +470,7 @@ namespace basisu
static_assert(NumBytes <= sizeof(uint32_t), "packed_uint too large to use get_uint32");
return static_cast<uint32_t>(get_uint64());
}
inline operator uint32_t() const
{
static_assert(NumBytes <= sizeof(uint32_t), "packed_uint too large to use operator uint32_t");
@@ -421,14 +480,14 @@ namespace basisu
enum eZero { cZero };
enum eNoClamp { cNoClamp };
// Rice/Huffman entropy coding
// This is basically Deflate-style canonical Huffman, except we allow for a lot more symbols.
enum
{
cHuffmanMaxSupportedCodeSize = 16, cHuffmanMaxSupportedInternalCodeSize = 31,
cHuffmanFastLookupBits = 10,
cHuffmanMaxSupportedCodeSize = 16, cHuffmanMaxSupportedInternalCodeSize = 31,
cHuffmanFastLookupBits = 10,
cHuffmanMaxSymsLog2 = 14, cHuffmanMaxSyms = 1 << cHuffmanMaxSymsLog2,
// Small zero runs
@@ -454,13 +513,13 @@ namespace basisu
enum class texture_format
{
cInvalidTextureFormat = -1,
// Block-based formats
cETC1, // ETC1
cETC1S, // ETC1 (subset: diff colors only, no subblocks)
cETC2_RGB, // ETC2 color block (basisu doesn't support ETC2 planar/T/H modes - just basic ETC1)
cETC2_RGBA, // ETC2 EAC alpha block followed by ETC2 color block
cETC2_ALPHA, // ETC2 EAC alpha block
cETC2_ALPHA, // ETC2 EAC alpha block
cBC1, // DXT1
cBC3, // DXT5 (BC4/DXT5A block followed by a BC1/DXT1 block)
cBC4, // DXT5A
@@ -479,11 +538,11 @@ namespace basisu
cPVRTC2_4_RGBA,
cETC2_R11_EAC,
cETC2_RG11_EAC,
cUASTC4x4,
cUASTC4x4,
cUASTC_HDR_4x4,
cBC1_NV,
cBC1_AMD,
// Uncompressed/raw pixels
cRGBA32,
cRGB565,
@@ -492,9 +551,89 @@ namespace basisu
cABGR4444,
cRGBA_HALF,
cRGB_HALF,
cRGB_9E5
cRGB_9E5,
// All remaining ASTC LDR block size variants (other than 4x4 which is above). There are 14 total ASTC block sizes, including 4x4.
cASTC_LDR_5x4,
cASTC_LDR_5x5,
cASTC_LDR_6x5,
cASTC_LDR_6x6,
cASTC_LDR_8x5,
cASTC_LDR_8x6,
cASTC_LDR_10x5,
cASTC_LDR_10x6,
cASTC_LDR_8x8,
cASTC_LDR_10x8,
cASTC_LDR_10x10,
cASTC_LDR_12x10,
cASTC_LDR_12x12
};
inline bool is_astc(texture_format fmt)
{
switch (fmt)
{
case texture_format::cASTC_HDR_4x4:
case texture_format::cASTC_HDR_6x6:
case texture_format::cASTC_LDR_4x4:
case texture_format::cASTC_LDR_5x4:
case texture_format::cASTC_LDR_5x5:
case texture_format::cASTC_LDR_6x5:
case texture_format::cASTC_LDR_6x6:
case texture_format::cASTC_LDR_8x5:
case texture_format::cASTC_LDR_8x6:
case texture_format::cASTC_LDR_10x5:
case texture_format::cASTC_LDR_10x6:
case texture_format::cASTC_LDR_8x8:
case texture_format::cASTC_LDR_10x8:
case texture_format::cASTC_LDR_10x10:
case texture_format::cASTC_LDR_12x10:
case texture_format::cASTC_LDR_12x12:
return true;
default:
break;
}
return false;
}
inline bool is_hdr_astc(texture_format fmt)
{
switch (fmt)
{
case texture_format::cASTC_HDR_4x4:
case texture_format::cASTC_HDR_6x6:
return true;
default:
break;
}
return false;
}
inline bool is_ldr_astc(texture_format fmt)
{
switch (fmt)
{
case texture_format::cASTC_LDR_4x4:
case texture_format::cASTC_LDR_5x4:
case texture_format::cASTC_LDR_5x5:
case texture_format::cASTC_LDR_6x5:
case texture_format::cASTC_LDR_6x6:
case texture_format::cASTC_LDR_8x5:
case texture_format::cASTC_LDR_8x6:
case texture_format::cASTC_LDR_10x5:
case texture_format::cASTC_LDR_10x6:
case texture_format::cASTC_LDR_8x8:
case texture_format::cASTC_LDR_10x8:
case texture_format::cASTC_LDR_10x10:
case texture_format::cASTC_LDR_12x10:
case texture_format::cASTC_LDR_12x12:
return true;
default:
break;
}
return false;
}
inline bool is_uncompressed_texture_format(texture_format fmt)
{
switch (fmt)
@@ -555,7 +694,7 @@ namespace basisu
default:
break;
}
// Everything else is 16 bytes/block.
return 16;
}
@@ -575,10 +714,21 @@ namespace basisu
switch (fmt)
{
case texture_format::cFXT1_RGB:
return 8;
case texture_format::cASTC_HDR_6x6:
return 6;
case texture_format::cFXT1_RGB: return 8;
case texture_format::cASTC_HDR_6x6: return 6;
case texture_format::cASTC_LDR_5x4: return 5;
case texture_format::cASTC_LDR_5x5: return 5;
case texture_format::cASTC_LDR_6x5: return 6;
case texture_format::cASTC_LDR_6x6: return 6;
case texture_format::cASTC_LDR_8x5: return 8;
case texture_format::cASTC_LDR_8x6: return 8;
case texture_format::cASTC_LDR_10x5: return 10;
case texture_format::cASTC_LDR_10x6: return 10;
case texture_format::cASTC_LDR_8x8: return 8;
case texture_format::cASTC_LDR_10x8: return 10;
case texture_format::cASTC_LDR_10x10: return 10;
case texture_format::cASTC_LDR_12x10: return 12;
case texture_format::cASTC_LDR_12x12: return 12;
default:
break;
}
@@ -591,8 +741,19 @@ namespace basisu
switch (fmt)
{
case texture_format::cASTC_HDR_6x6:
return 6;
case texture_format::cASTC_HDR_6x6: return 6;
case texture_format::cASTC_LDR_5x5: return 5;
case texture_format::cASTC_LDR_6x5: return 5;
case texture_format::cASTC_LDR_6x6: return 6;
case texture_format::cASTC_LDR_8x5: return 5;
case texture_format::cASTC_LDR_8x6: return 6;
case texture_format::cASTC_LDR_10x5: return 5;
case texture_format::cASTC_LDR_10x6: return 6;
case texture_format::cASTC_LDR_8x8: return 8;
case texture_format::cASTC_LDR_10x8: return 8;
case texture_format::cASTC_LDR_10x10: return 10;
case texture_format::cASTC_LDR_12x10: return 10;
case texture_format::cASTC_LDR_12x12: return 12;
default:
break;
}
@@ -623,5 +784,38 @@ namespace basisu
{
return !is_hdr_texture_format(fmt);
}
inline texture_format get_astc_ldr_texture_format(uint32_t width, uint32_t height)
{
#define BU_ASTC_LDR_MATCH_BLOCK_DIM(x, y, f) if ((width == (x)) && (height == (y))) return (f);
BU_ASTC_LDR_MATCH_BLOCK_DIM(4, 4, texture_format::cASTC_LDR_4x4);
BU_ASTC_LDR_MATCH_BLOCK_DIM(5, 4, texture_format::cASTC_LDR_5x4);
BU_ASTC_LDR_MATCH_BLOCK_DIM(5, 5, texture_format::cASTC_LDR_5x5);
BU_ASTC_LDR_MATCH_BLOCK_DIM(6, 5, texture_format::cASTC_LDR_6x5);
BU_ASTC_LDR_MATCH_BLOCK_DIM(6, 6, texture_format::cASTC_LDR_6x6);
BU_ASTC_LDR_MATCH_BLOCK_DIM(8, 5, texture_format::cASTC_LDR_8x5);
BU_ASTC_LDR_MATCH_BLOCK_DIM(8, 6, texture_format::cASTC_LDR_8x6);
BU_ASTC_LDR_MATCH_BLOCK_DIM(10, 5, texture_format::cASTC_LDR_10x5);
BU_ASTC_LDR_MATCH_BLOCK_DIM(10, 6, texture_format::cASTC_LDR_10x6);
BU_ASTC_LDR_MATCH_BLOCK_DIM(8, 8, texture_format::cASTC_LDR_8x8);
BU_ASTC_LDR_MATCH_BLOCK_DIM(10, 8, texture_format::cASTC_LDR_10x8);
BU_ASTC_LDR_MATCH_BLOCK_DIM(10, 10, texture_format::cASTC_LDR_10x10);
BU_ASTC_LDR_MATCH_BLOCK_DIM(12, 10, texture_format::cASTC_LDR_12x10);
BU_ASTC_LDR_MATCH_BLOCK_DIM(12, 12, texture_format::cASTC_LDR_12x12);
#undef BU_ASTC_LDR_MATCH_BLOCK_DIM
return texture_format::cInvalidTextureFormat;
}
inline bool is_valid_astc_block_size(uint32_t width, uint32_t height)
{
return get_astc_ldr_texture_format(width, height) != texture_format::cInvalidTextureFormat;
}
} // namespace basisu

View File

@@ -1,6 +1,5 @@
// File: basisu_astc_hdr_core.h
#pragma once
#include "basisu_astc_helpers.h"
namespace basist
{
@@ -109,7 +108,10 @@ namespace basist
const uint32_t TOTAL_BLOCK_MODE_DECS = 75;
extern const block_mode_desc g_block_mode_descs[TOTAL_BLOCK_MODE_DECS];
void copy_weight_grid(bool dual_plane, uint32_t grid_x, uint32_t grid_y, const uint8_t* transcode_weights, astc_helpers::log_astc_block& decomp_blk);
const uint32_t UASTC_6x6_HDR_SIG0 = 0xABCD; // original release
const uint32_t UASTC_6x6_HDR_SIG1 = 0xABCE; // 2x2->4x4 weight grid upsampling change, not backwards compatible with older decoders
void copy_weight_grid(bool dual_plane, uint32_t grid_x, uint32_t grid_y, const uint8_t* transcode_weights, astc_helpers::log_astc_block& decomp_blk, bool orig_behavior);
enum class encoding_type
{
@@ -181,7 +183,7 @@ namespace basist
bool m_hq_ls;
bool m_brute_force_weight4_assignment;
fast_bc6h_params()
{
init();
@@ -203,3 +205,4 @@ namespace basist
} // namespace astc_6x6_hdr
} // namespace basist

File diff suppressed because it is too large Load Diff

View File

@@ -159,8 +159,8 @@ namespace basisu
static inline void construct(T** p) { memset(p, 0, sizeof(T*)); }
static inline void construct(T** p, T* init) { *p = init; }
static inline void construct_array(T** p, size_t n) { memset(p, 0, sizeof(T*) * n); }
static inline void destruct(T** p) { p; }
static inline void destruct_array(T** p, size_t n) { p, n; }
static inline void destruct(T** p) { (void)p; }
static inline void destruct_array(T** p, size_t n) { (void)p, (void)n; }
};
#define BASISU_DEFINE_BUILT_IN_TYPE(X) \
@@ -169,8 +169,8 @@ namespace basisu
static inline void construct(X* p) { memset(p, 0, sizeof(X)); } \
static inline void construct(X* p, const X& init) { memcpy(p, &init, sizeof(X)); } \
static inline void construct_array(X* p, size_t n) { memset(p, 0, sizeof(X) * n); } \
static inline void destruct(X* p) { p; } \
static inline void destruct_array(X* p, size_t n) { p, n; } };
static inline void destruct(X* p) { (void)p; } \
static inline void destruct_array(X* p, size_t n) { (void)p, (void)n; } };
BASISU_DEFINE_BUILT_IN_TYPE(bool)
BASISU_DEFINE_BUILT_IN_TYPE(char)
@@ -272,7 +272,7 @@ namespace basisu
size_t c = a + b;
return c < a;
}
// Returns false on overflow, true if OK.
template<typename T>
inline bool can_fit_into_size_t(T val)
@@ -294,7 +294,7 @@ namespace basisu
template<typename T>
class writable_span;
template<typename T>
class readable_span
{
@@ -304,7 +304,7 @@ namespace basisu
using const_pointer = const T*;
using const_reference = const T&;
using const_iterator = const T*;
inline readable_span() :
m_p(nullptr),
m_size(0)
@@ -941,7 +941,7 @@ namespace basisu
inline iterator begin() const { return m_p; }
inline iterator end() const { assert(m_p || !m_size); return m_p + m_size; }
inline const_iterator cbegin() const { return m_p; }
inline const_iterator cend() const { assert(m_p || !m_size); return m_p + m_size; }
@@ -1506,7 +1506,7 @@ namespace basisu
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#endif
if ((m_p) && (other.m_p))
{
@@ -1563,6 +1563,52 @@ namespace basisu
set(ws);
}
// mostly to ease porting from std::vector, not particularly optimized
inline void assign(size_t new_size, const T& init)
{
assert(!m_p || (&init < m_p) || (&init >= (m_p + m_size)));
// Blow away existing contents
resize(0);
if (new_size)
{
resize(new_size);
for (size_t i = 0; i < new_size; ++i)
m_p[i] = init;
}
}
// mostly to ease porting from std::vector, not particularly optimized
template<typename R>
inline void assign(const R* pBegin, const R* pEnd)
{
assert(!m_p ||
(reinterpret_cast<const uint8_t *>(pEnd) <= reinterpret_cast<const uint8_t*>(m_p)) ||
(reinterpret_cast<const uint8_t *>(pBegin) >= reinterpret_cast<const uint8_t*>(m_p + m_size))
);
// Blow away existing contents
resize(0);
if ((!pBegin) || (!pEnd) || (pEnd <= pBegin))
{
assert(0);
return;
}
const size_t new_size = static_cast<size_t>(static_cast<ptrdiff_t>(pEnd - pBegin));
if (new_size)
{
resize(new_size);
for (size_t i = 0; i < new_size; ++i)
m_p[i] = static_cast<T>(*pBegin++);
}
}
// Set contents of vector to contents of the readable span
bool set(const readable_span<T>& rs)
{
@@ -1647,7 +1693,7 @@ namespace basisu
{
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#endif
if ((m_p) && (other.m_p))
memcpy((void *)m_p, other.m_p, other.m_size * sizeof(T));
@@ -2147,7 +2193,7 @@ namespace basisu
if (!try_insert(p, obj))
container_abort("vector::insert() failed!\n");
}
// push_front() isn't going to be very fast - it's only here for usability.
inline void push_front(const T& obj)
{
@@ -2228,7 +2274,7 @@ namespace basisu
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#endif
memmove((void *)pDst, pSrc, num_to_move * sizeof(T));
@@ -2239,7 +2285,7 @@ namespace basisu
}
else
{
// Type is not bitwise copyable or movable.
// Type is not bitwise copyable or movable.
// Move them down one at a time by using the equals operator, and destroying anything that's left over at the end.
T* pDst_end = pDst + num_to_move;
@@ -2482,7 +2528,7 @@ namespace basisu
{
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#endif
memset(m_p, *reinterpret_cast<const uint8_t*>(&o), m_size);
@@ -2770,6 +2816,7 @@ namespace basisu
m_grow_threshold = 0;
}
// Destroys elements/empties container but doesn't free memory.
inline void reset()
{
if (!m_num_valid)
@@ -2798,7 +2845,7 @@ namespace basisu
}
else if (sizeof(node) <= 16)
{
memset(&m_values[0], 0, m_values.size_in_bytes());
memset((void *)&m_values[0], 0, m_values.size_in_bytes());
}
else
{
@@ -2829,6 +2876,11 @@ namespace basisu
return m_num_valid;
}
inline uint32_t size_u32()
{
return static_cast<uint32_t>(m_num_valid);
}
inline size_t get_table_size()
{
return m_values.size();
@@ -3102,7 +3154,7 @@ namespace basisu
{
return try_insert(result, std::move(v.first), std::move(v.second));
}
inline const_iterator find(const Key& k) const
{
return const_iterator(*this, find_index(k));
@@ -3183,12 +3235,12 @@ namespace basisu
static inline void construct_value_type(value_type* pDst, const Key& k, const Value& v)
{
if (BASISU_IS_BITWISE_COPYABLE(Key))
memcpy(&pDst->first, &k, sizeof(Key));
memcpy((void *)&pDst->first, &k, sizeof(Key));
else
scalar_type<Key>::construct(&pDst->first, k);
if (BASISU_IS_BITWISE_COPYABLE(Value))
memcpy(&pDst->second, &v, sizeof(Value));
memcpy((void *)&pDst->second, &v, sizeof(Value));
else
scalar_type<Value>::construct(&pDst->second, v);
}
@@ -3197,17 +3249,17 @@ namespace basisu
{
if ((BASISU_IS_BITWISE_COPYABLE(Key)) && (BASISU_IS_BITWISE_COPYABLE(Value)))
{
memcpy(pDst, pSrc, sizeof(value_type));
memcpy((void *)pDst, pSrc, sizeof(value_type));
}
else
{
if (BASISU_IS_BITWISE_COPYABLE(Key))
memcpy(&pDst->first, &pSrc->first, sizeof(Key));
memcpy((void *)&pDst->first, &pSrc->first, sizeof(Key));
else
scalar_type<Key>::construct(&pDst->first, pSrc->first);
if (BASISU_IS_BITWISE_COPYABLE(Value))
memcpy(&pDst->second, &pSrc->second, sizeof(Value));
memcpy((void *)&pDst->second, &pSrc->second, sizeof(Value));
else
scalar_type<Value>::construct(&pDst->second, pSrc->second);
}
@@ -3227,14 +3279,14 @@ namespace basisu
if (BASISU_IS_BITWISE_COPYABLE_OR_MOVABLE(Key) && BASISU_IS_BITWISE_COPYABLE_OR_MOVABLE(Value))
{
memcpy(pDst, pSrc, sizeof(node));
memcpy((void *)pDst, pSrc, sizeof(node));
assert(pDst->state == cStateValid);
}
else
{
if (BASISU_IS_BITWISE_COPYABLE_OR_MOVABLE(Key))
memcpy(&pDst->first, &pSrc->first, sizeof(Key));
memcpy((void*)&pDst->first, &pSrc->first, sizeof(Key));
else
{
new ((void*)&pDst->first) Key(std::move(pSrc->first));
@@ -3242,7 +3294,7 @@ namespace basisu
}
if (BASISU_IS_BITWISE_COPYABLE_OR_MOVABLE(Value))
memcpy(&pDst->second, &pSrc->second, sizeof(Value));
memcpy((void*)&pDst->second, &pSrc->second, sizeof(Value));
else
{
new ((void*)&pDst->second) Value(std::move(pSrc->second));
@@ -3583,7 +3635,7 @@ namespace basisu
// Not checking for is MOVABLE because the caller could later destruct k and/or v (what state do we set them to?)
if (BASISU_IS_BITWISE_COPYABLE(Key))
{
memcpy(&pDst->first, &k, sizeof(Key));
memcpy((void *)&pDst->first, (const void *)&k, sizeof(Key));
}
else
{
@@ -3593,7 +3645,7 @@ namespace basisu
if (BASISU_IS_BITWISE_COPYABLE(Value))
{
memcpy(&pDst->second, &v, sizeof(Value));
memcpy((void *)&pDst->second, (const void*)&v, sizeof(Value));
}
else
{
@@ -3721,11 +3773,11 @@ namespace basisu
va_list args;
va_start(args, pFmt);
#ifdef _WIN32
#ifdef _WIN32
vsprintf_s(buf, sizeof(buf), pFmt, args);
#else
vsnprintf(buf, sizeof(buf), pFmt, args);
#endif
#endif
va_end(args);
return std::string(buf);
@@ -3893,7 +3945,7 @@ namespace basisu
std::size_t copy_size = std::min(list.size(), N);
std::copy_n(list.begin(), copy_size, m_data); // Copy up to min(list.size(), N)
if (list.size() < N)
if (list.size() < N)
{
// Initialize the rest of the array
std::fill(m_data + copy_size, m_data + N, T{});
@@ -3907,7 +3959,7 @@ namespace basisu
return m_data[index];
}
BASISU_FORCE_INLINE const T& operator[](std::size_t index) const
BASISU_FORCE_INLINE const T& operator[](std::size_t index) const
{
if (index >= N)
container_abort("fixed_array: Index out of bounds.");
@@ -3950,26 +4002,26 @@ namespace basisu
{
return writable_span<T>(m_data, N);
}
private:
BASISU_FORCE_INLINE void initialize_array()
{
if constexpr (std::is_integral<T>::value || std::is_floating_point<T>::value)
if constexpr (std::is_integral<T>::value || std::is_floating_point<T>::value)
memset(m_data, 0, sizeof(m_data));
else
else
std::fill(m_data, m_data + N, T{});
}
BASISU_FORCE_INLINE T& access_element(std::size_t index)
{
if (index >= N)
if (index >= N)
container_abort("fixed_array: Index out of bounds.");
return m_data[index];
}
BASISU_FORCE_INLINE const T& access_element(std::size_t index) const
{
if (index >= N)
if (index >= N)
container_abort("fixed_array: Index out of bounds.");
return m_data[index];
}
@@ -4046,6 +4098,9 @@ namespace basisu
inline uint32_t get_width() const { return m_width; }
inline uint32_t get_height() const { return m_height; }
inline uint32_t get_cols() const { return m_width; }
inline uint32_t get_rows() const { return m_height; }
inline const T& operator() (uint32_t x, uint32_t y) const { assert(x < m_width && y < m_height); return m_values[x + y * m_width]; }
inline T& operator() (uint32_t x, uint32_t y) { assert(x < m_width && y < m_height); return m_values[x + y * m_width]; }
@@ -4054,9 +4109,23 @@ namespace basisu
inline const T& operator[] (uint32_t i) const { return m_values[i]; }
inline T& operator[] (uint32_t i) { return m_values[i]; }
inline const T& at(int x, int y) const { return (*this)((uint32_t)x, (uint32_t)y); }
inline T& at(int x, int y) { return (*this)((uint32_t)x, (uint32_t)y); }
inline const T& at_clamped(int x, int y) const { return (*this)(clamp<int>(x, 0, m_width - 1), clamp<int>(y, 0, m_height - 1)); }
inline T& at_clamped(int x, int y) { return (*this)(clamp<int>(x, 0, m_width - 1), clamp<int>(y, 0, m_height - 1)); }
inline const T& at_row_col(int y, int x) const { return (*this)(clamp<int>(x, 0, m_width - 1), clamp<int>(y, 0, m_height - 1)); }
inline T& at_row_col(int y, int x) { return (*this)(clamp<int>(x, 0, m_width - 1), clamp<int>(y, 0, m_height - 1)); }
inline void set_clipped(int x, int y, const T& val)
{
if ( ((uint32_t)x >= m_width) || ((uint32_t)y >= m_height) )
return;
m_values[x + y * m_width] = val;
}
void clear()
{
m_width = 0;
@@ -4141,9 +4210,18 @@ namespace basisu
return true;
}
vector2D& resize_rows_cols(uint32_t rows, uint32_t cols)
{
return resize(cols, rows);
}
bool try_resize_rows_cols(uint32_t rows, uint32_t cols)
{
return try_resize(cols, rows);
}
const vector2D& extract_block_clamped(T* pDst, uint32_t src_x, uint32_t src_y, uint32_t w, uint32_t h) const
{
// HACK HACK
if (((src_x + w) > m_width) || ((src_y + h) > m_height))
{
// Slower clamping case
@@ -4165,8 +4243,87 @@ namespace basisu
return *this;
}
const vector2D& extract_block_clamped(T* pDst, uint32_t src_x, uint32_t src_y, uint32_t w, uint32_t h, uint32_t override_height) const
{
assert(override_height && (override_height <= m_height));
if (((src_x + w) > m_width) || ((src_y + h) > minimum(m_height, override_height)))
{
// Slower clamping case
for (uint32_t y = 0; y < h; y++)
for (uint32_t x = 0; x < w; x++)
*pDst++ = at_clamped(src_x + x, minimum(src_y + y, override_height - 1));
}
else
{
const T* pSrc = &m_values[src_x + src_y * m_width];
for (uint32_t y = 0; y < h; y++)
{
memcpy(pDst, pSrc, w * sizeof(T));
pSrc += m_width;
pDst += w;
}
}
return *this;
}
};
// Explictly primitive container intended for POD's, simple usage.
// push_back() and resize() will refuse to push anymore and just return when full.
template<typename T, uint32_t N>
class static_vector
{
T m_data[N];
uint32_t m_size;
public:
static_vector() : m_size(0) { }
inline void reserve(size_t reserve_size)
{
(void)(reserve_size);
assert(reserve_size <= N);
}
inline void push_back(const T& value)
{
// Should never happen.
if (m_size >= N)
{
assert(0);
fprintf(stderr, "basisu::static_vector overflow!\n");
return;
}
m_data[m_size++] = value;
}
inline std::size_t size() const { return m_size; }
inline uint32_t size_u32() const { return m_size; }
inline constexpr std::size_t capacity() const { return N; }
inline bool empty() const { return !m_size; }
inline T& operator[](std::size_t i) { return m_data[i]; }
inline const T& operator[](std::size_t i) const { return m_data[i]; }
inline void resize(size_t new_size)
{
if (new_size > N)
{
assert(0);
fprintf(stderr, "basisu::static_vector overflow!\n");
return;
}
m_size = (uint32_t)new_size;
}
};
} // namespace basisu
namespace std

View File

@@ -14,7 +14,7 @@ namespace basisu
#ifdef _MSC_VER
__declspec(noreturn)
#else
[[noreturn]]
[[noreturn]]
#endif
void container_abort(const char* pMsg, ...)
{
@@ -42,12 +42,12 @@ namespace basisu
assert(m_size <= m_capacity);
assert(min_new_capacity >= m_size);
assert(element_size);
// Basic sanity check min_new_capacity
if (!can_fit_into_size_t((uint64_t)min_new_capacity * element_size))
{
assert(0);
if (nofail_flag)
return false;
@@ -100,7 +100,7 @@ namespace basisu
}
const size_t desired_size = static_cast<size_t>(desired_size_u64);
size_t actual_size = 0;
BASISU_NOTE_UNUSED(actual_size);
@@ -109,6 +109,7 @@ namespace basisu
void* new_p = realloc(m_p, desired_size);
if (!new_p)
{
fprintf(stderr, "elemental_vector::increase_capacity: Allocation failed!\n");
assert(0);
if (nofail_flag)
@@ -133,7 +134,9 @@ namespace basisu
void* new_p = malloc(desired_size);
if (!new_p)
{
fprintf(stderr, "elemental_vector::increase_capacity: Allocation failed!\n");
assert(0);
if (nofail_flag)
return false;
@@ -269,7 +272,7 @@ namespace basisu
s.insert(i);
k.push_back(i);
}
for (uint32_t i = 0; i < k.size(); i++)
{
uint32_t r = rand() ^ (rand() << 15);
@@ -315,7 +318,7 @@ namespace basisu
{
typedef basisu::hash_map< uint32_t, basisu::vector<uint32_t> > hm;
hm q;
basisu::vector<uint32_t> a, b;
a.push_back(1);
b.push_back(2);

View File

@@ -1,5 +1,5 @@
// basis_file_headers.h
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2026 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -39,7 +39,7 @@ namespace basist
basisu::packed_uint<2> m_orig_height; // The original image height (may not be a multiple of 4 pixels)
basisu::packed_uint<2> m_num_blocks_x; // The slice's block X dimensions. Each block is 4x4 or 6x6 pixels. The slice's pixel resolution may or may not be a power of 2.
basisu::packed_uint<2> m_num_blocks_y; // The slice's block Y dimensions.
basisu::packed_uint<2> m_num_blocks_y; // The slice's block Y dimensions.
basisu::packed_uint<4> m_file_ofs; // Offset from the start of the file to the start of the slice's data
basisu::packed_uint<4> m_file_size; // The size of the compressed slice data in bytes
@@ -59,16 +59,16 @@ namespace basist
// Set if any slices contain alpha (for ETC1S, if the odd slices contain alpha data)
cBASISHeaderFlagHasAlphaSlices = 4,
// For ETC1S files, this will be true if the file utilizes a codebook from another .basis file.
// For ETC1S files, this will be true if the file utilizes a codebook from another .basis file.
cBASISHeaderFlagUsesGlobalCodebook = 8,
// Set if the texture data is sRGB, otherwise it's linear.
// Set if the texture data is sRGB, otherwise it's linear.
// In reality, we have no idea if the texture data is actually linear or sRGB. This is the m_perceptual parameter passed to the compressor.
cBASISHeaderFlagSRGB = 16,
};
// The image type field attempts to describe how to interpret the image data in a Basis file.
// The encoder library doesn't really do anything special or different with these texture types, this is mostly here for the benefit of the user.
// The encoder library doesn't really do anything special or different with these texture types, this is mostly here for the benefit of the user.
// We do make sure the various constraints are followed (2DArray/cubemap/videoframes/volume implies that each image has the same resolution and # of mipmap levels, etc., cubemap implies that the # of image slices is a multiple of 6)
enum basis_texture_type
{
@@ -88,14 +88,113 @@ namespace basist
enum class basis_tex_format
{
// Original LDR formats
cETC1S = 0,
cUASTC4x4 = 1,
cUASTC_LDR_4x4 = 1,
// HDR formats
cUASTC_HDR_4x4 = 2,
cASTC_HDR_6x6 = 3,
cASTC_HDR_6x6_INTERMEDIATE = 4,
cUASTC_HDR_6x6_INTERMEDIATE = 4, // TODO: rename to UASTC_HDR_6x6
// XUASTC (supercompressed) LDR variants (the standard ASTC block sizes)
cXUASTC_LDR_4x4 = 5,
cXUASTC_LDR_5x4 = 6,
cXUASTC_LDR_5x5 = 7,
cXUASTC_LDR_6x5 = 8,
cXUASTC_LDR_6x6 = 9,
cXUASTC_LDR_8x5 = 10,
cXUASTC_LDR_8x6 = 11,
cXUASTC_LDR_10x5 = 12,
cXUASTC_LDR_10x6 = 13,
cXUASTC_LDR_8x8 = 14,
cXUASTC_LDR_10x8 = 15,
cXUASTC_LDR_10x10 = 16,
cXUASTC_LDR_12x10 = 17,
cXUASTC_LDR_12x12 = 18,
// Standard (non-supercompressed) ASTC LDR variants (the standard ASTC block sizes)
cASTC_LDR_4x4 = 19,
cASTC_LDR_5x4 = 20,
cASTC_LDR_5x5 = 21,
cASTC_LDR_6x5 = 22,
cASTC_LDR_6x6 = 23,
cASTC_LDR_8x5 = 24,
cASTC_LDR_8x6 = 25,
cASTC_LDR_10x5 = 26,
cASTC_LDR_10x6 = 27,
cASTC_LDR_8x8 = 28,
cASTC_LDR_10x8 = 29,
cASTC_LDR_10x10 = 30,
cASTC_LDR_12x10 = 31,
cASTC_LDR_12x12 = 32,
cTotalFormats
};
// True if the basis_tex_format is XUASTC LDR 4x4-12x12.
inline bool basis_tex_format_is_xuastc_ldr(basis_tex_format tex_fmt)
{
return ((uint32_t)tex_fmt >= (uint32_t)basis_tex_format::cXUASTC_LDR_4x4) && ((uint32_t)tex_fmt <= (uint32_t)basis_tex_format::cXUASTC_LDR_12x12);
}
// True if the basis_tex_format is ASTC LDR 4x4-12x12.
inline bool basis_tex_format_is_astc_ldr(basis_tex_format tex_fmt)
{
return ((uint32_t)tex_fmt >= (uint32_t)basis_tex_format::cASTC_LDR_4x4) && ((uint32_t)tex_fmt <= (uint32_t)basis_tex_format::cASTC_LDR_12x12);
}
inline void get_basis_tex_format_block_size(basis_tex_format tex_fmt, uint32_t &width, uint32_t &height)
{
switch (tex_fmt)
{
case basis_tex_format::cETC1S: width = 4; height = 4; break;
case basis_tex_format::cUASTC_LDR_4x4: width = 4; height = 4; break;
case basis_tex_format::cUASTC_HDR_4x4: width = 4; height = 4; break;
case basis_tex_format::cASTC_HDR_6x6: width = 6; height = 6; break;
case basis_tex_format::cUASTC_HDR_6x6_INTERMEDIATE: width = 6; height = 6; break;
case basis_tex_format::cXUASTC_LDR_4x4: width = 4; height = 4; break;
case basis_tex_format::cXUASTC_LDR_5x4: width = 5; height = 4; break;
case basis_tex_format::cXUASTC_LDR_5x5: width = 5; height = 5; break;
case basis_tex_format::cXUASTC_LDR_6x5: width = 6; height = 5; break;
case basis_tex_format::cXUASTC_LDR_6x6: width = 6; height = 6; break;
case basis_tex_format::cXUASTC_LDR_8x5: width = 8; height = 5; break;
case basis_tex_format::cXUASTC_LDR_8x6: width = 8; height = 6; break;
case basis_tex_format::cXUASTC_LDR_10x5: width = 10; height = 5; break;
case basis_tex_format::cXUASTC_LDR_10x6: width = 10; height = 6; break;
case basis_tex_format::cXUASTC_LDR_8x8: width = 8; height = 8; break;
case basis_tex_format::cXUASTC_LDR_10x8: width = 10; height = 8; break;
case basis_tex_format::cXUASTC_LDR_10x10: width = 10; height = 10; break;
case basis_tex_format::cXUASTC_LDR_12x10: width = 12; height = 10; break;
case basis_tex_format::cXUASTC_LDR_12x12: width = 12; height = 12; break;
case basis_tex_format::cASTC_LDR_4x4: width = 4; height = 4; break;
case basis_tex_format::cASTC_LDR_5x4: width = 5; height = 4; break;
case basis_tex_format::cASTC_LDR_5x5: width = 5; height = 5; break;
case basis_tex_format::cASTC_LDR_6x5: width = 6; height = 5; break;
case basis_tex_format::cASTC_LDR_6x6: width = 6; height = 6; break;
case basis_tex_format::cASTC_LDR_8x5: width = 8; height = 5; break;
case basis_tex_format::cASTC_LDR_8x6: width = 8; height = 6; break;
case basis_tex_format::cASTC_LDR_10x5: width = 10; height = 5; break;
case basis_tex_format::cASTC_LDR_10x6: width = 10; height = 6; break;
case basis_tex_format::cASTC_LDR_8x8: width = 8; height = 8; break;
case basis_tex_format::cASTC_LDR_10x8: width = 10; height = 8; break;
case basis_tex_format::cASTC_LDR_10x10: width = 10; height = 10; break;
case basis_tex_format::cASTC_LDR_12x10: width = 12; height = 10; break;
case basis_tex_format::cASTC_LDR_12x12: width = 12; height = 12; break;
default:
assert(0);
width = 0;
height = 0;
break;
}
}
struct basis_file_header
{
enum
@@ -115,7 +214,7 @@ namespace basist
basisu::packed_uint<3> m_total_slices; // The total # of compressed slices (1 slice per image, or 2 for alpha .basis files)
basisu::packed_uint<3> m_total_images; // The total # of images
basisu::packed_uint<1> m_tex_format; // enum basis_tex_format
basisu::packed_uint<2> m_flags; // enum basist::header_flags
basisu::packed_uint<1> m_tex_type; // enum basist::basis_texture_type
@@ -125,11 +224,11 @@ namespace basist
basisu::packed_uint<4> m_userdata0; // For client use
basisu::packed_uint<4> m_userdata1; // For client use
basisu::packed_uint<2> m_total_endpoints; // The number of endpoints in the endpoint codebook
basisu::packed_uint<2> m_total_endpoints; // The number of endpoints in the endpoint codebook
basisu::packed_uint<4> m_endpoint_cb_file_ofs; // The compressed endpoint codebook's file offset relative to the start of the file
basisu::packed_uint<3> m_endpoint_cb_file_size; // The compressed endpoint codebook's size in bytes
basisu::packed_uint<2> m_total_selectors; // The number of selectors in the endpoint codebook
basisu::packed_uint<2> m_total_selectors; // The number of selectors in the endpoint codebook
basisu::packed_uint<4> m_selector_cb_file_ofs; // The compressed selectors codebook's file offset relative to the start of the file
basisu::packed_uint<3> m_selector_cb_file_size; // The compressed selector codebook's size in bytes
@@ -137,7 +236,7 @@ namespace basist
basisu::packed_uint<4> m_tables_file_size; // The file size in bytes of the compressed huffman codelength tables
basisu::packed_uint<4> m_slice_desc_file_ofs; // The file offset to the slice description array, usually follows the header
basisu::packed_uint<4> m_extended_file_ofs; // The file offset of the "extended" header and compressed data, for future use
basisu::packed_uint<4> m_extended_file_size; // The file size in bytes of the "extended" header and compressed data, for future use
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
// basisu_transcoder.h
// Copyright (C) 2019-2024 Binomial LLC. All Rights Reserved.
// Copyright (C) 2019-2026 Binomial LLC. All Rights Reserved.
// Important: If compiling with gcc, be sure strict aliasing is disabled: -fno-strict-aliasing
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,6 +13,8 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Also see basis_tex_format in basisu_file_headers.h (TODO: Perhaps move key definitions into here.)
#pragma once
// By default KTX2 support is enabled to simplify compilation. This implies the need for the Zstandard library (which we distribute as a single source file in the "zstd" directory) by default.
@@ -22,17 +24,11 @@
#define BASISD_SUPPORT_KTX2 1
#endif
// Set BASISD_SUPPORT_KTX2_ZSTD to 0 to disable Zstd usage and KTX2 UASTC Zstd supercompression support
// Set BASISD_SUPPORT_KTX2_ZSTD to 0 to disable Zstd usage and KTX2 UASTC Zstd supercompression support
#ifndef BASISD_SUPPORT_KTX2_ZSTD
#define BASISD_SUPPORT_KTX2_ZSTD 1
#endif
// Set BASISU_FORCE_DEVEL_MESSAGES to 1 to enable debug printf()'s whenever an error occurs, for easier debugging during development.
#ifndef BASISU_FORCE_DEVEL_MESSAGES
// TODO - disable before checking in
#define BASISU_FORCE_DEVEL_MESSAGES 0
#endif
#include "basisu_transcoder_internal.h"
#include "basisu_transcoder_uastc.h"
#include "basisu_file_headers.h"
@@ -42,7 +38,7 @@ namespace basist
// High-level composite texture formats supported by the transcoder.
// Each of these texture formats directly correspond to OpenGL/D3D/Vulkan etc. texture formats.
// Notes:
// - If you specify a texture format that supports alpha, but the .basis file doesn't have alpha, the transcoder will automatically output a
// - If you specify a texture format that supports alpha, but the .basis file doesn't have alpha, the transcoder will automatically output a
// fully opaque (255) alpha channel.
// - The PVRTC1 texture formats only support power of 2 dimension .basis files, but this may be relaxed in a future version.
// - The PVRTC1 transcoders are real-time encoders, so don't expect the highest quality. We may add a slower encoder with improved quality.
@@ -66,13 +62,13 @@ namespace basist
cTFPVRTC1_4_RGB = 8, // Opaque only, RGB or alpha if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified, nearly lowest quality of any texture format.
cTFPVRTC1_4_RGBA = 9, // Opaque+alpha, most useful for simple opacity maps. If .basis file doesn't have alpha cTFPVRTC1_4_RGB will be used instead. Lowest quality of any supported texture format.
// ASTC (mobile, Intel devices, hopefully all desktop GPU's one day)
cTFASTC_4x4_RGBA = 10, // LDR. Opaque+alpha, ASTC 4x4, alpha channel will be opaque for opaque .basis files.
// ASTC (mobile, some Intel CPU's, hopefully all desktop GPU's one day)
cTFASTC_LDR_4x4_RGBA = 10, // LDR. Opaque+alpha, ASTC 4x4, alpha channel will be opaque for opaque .basis files.
// LDR: Transcoder uses RGB/RGBA/L/LA modes, void extent, and up to two ([0,47] and [0,255]) endpoint precisions.
// ATC (mobile, Adreno devices, this is a niche format)
cTFATC_RGB = 11, // Opaque, RGB or alpha if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified. ATI ATC (GL_ATC_RGB_AMD)
cTFATC_RGBA = 12, // Opaque+alpha, alpha channel will be opaque for opaque .basis files. ATI ATC (GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD)
cTFATC_RGBA = 12, // Opaque+alpha, alpha channel will be opaque for opaque .basis files. ATI ATC (GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD)
// FXT1 (desktop, Intel devices, this is a super obscure format)
cTFFXT1_RGB = 17, // Opaque only, uses exclusively CC_MIXED blocks. Notable for having a 8x4 block size. GL_3DFX_texture_compression_FXT1 is supported on Intel integrated GPU's (such as HD 630).
@@ -94,7 +90,7 @@ namespace basist
cTFRGB565 = 14, // 16bpp RGB image stored in raster (not block) order in memory, R at bit position 11
cTFBGR565 = 15, // 16bpp RGB image stored in raster (not block) order in memory, R at bit position 0
cTFRGBA4444 = 16, // 16bpp RGBA image stored in raster (not block) order in memory, R at bit position 12, A at bit position 0
// Note these uncompressed formats (HALF and 9E5) can only be transcoded to from HDR input files (UASTC HDR 4x4 or ASTC HDR 6x6).
cTFRGB_HALF = 24, // 48bpp RGB half (16-bits/component, 3 components)
cTFRGBA_HALF = 25, // 64bpp RGBA half (16-bits/component, 4 components) (A will always currently 1.0, UASTC_HDR doesn't support alpha)
@@ -102,7 +98,23 @@ namespace basist
cTFASTC_HDR_6x6_RGBA = 27, // HDR, RGBA (currently our ASTC HDR 6x6 encodes are only RGB), unsigned
cTFTotalTextureFormats = 28,
// The remaining LDR ASTC block sizes, excluding 4x4 (which is above). There are 14 total valid ASTC LDR/HDR block sizes.
cTFASTC_LDR_5x4_RGBA = 28,
cTFASTC_LDR_5x5_RGBA = 29,
cTFASTC_LDR_6x5_RGBA = 30,
cTFASTC_LDR_6x6_RGBA = 31,
cTFASTC_LDR_8x5_RGBA = 32,
cTFASTC_LDR_8x6_RGBA = 33,
cTFASTC_LDR_10x5_RGBA = 34,
cTFASTC_LDR_10x6_RGBA = 35,
cTFASTC_LDR_8x8_RGBA = 36,
cTFASTC_LDR_10x8_RGBA = 37,
cTFASTC_LDR_10x10_RGBA = 38,
cTFASTC_LDR_12x10_RGBA = 39,
cTFASTC_LDR_12x12_RGBA = 40,
cTFTotalTextureFormats = 41,
// ----- The following are old/legacy enums for compatibility with code compiled against previous versions
cTFETC1 = cTFETC1_RGB,
@@ -112,25 +124,30 @@ namespace basist
cTFBC4 = cTFBC4_R,
cTFBC5 = cTFBC5_RG,
// Previously, the caller had some control over which BC7 mode the transcoder output. We've simplified this due to UASTC, which supports numerous modes.
// Previously, the caller had some control over which BC7 mode the transcoder output. We've simplified this due to UASTC LDR 4x4, which supports numerous modes.
cTFBC7_M6_RGB = cTFBC7_RGBA, // Opaque only, RGB or alpha if cDecodeFlagsTranscodeAlphaDataToOpaqueFormats flag is specified. Highest quality of all the non-ETC1 formats.
cTFBC7_M5_RGBA = cTFBC7_RGBA, // Opaque+alpha, alpha channel will be opaque for opaque .basis files
cTFBC7_M6_OPAQUE_ONLY = cTFBC7_RGBA,
cTFBC7_M5 = cTFBC7_RGBA,
cTFBC7_ALT = 7,
cTFASTC_4x4 = cTFASTC_4x4_RGBA,
cTFASTC_4x4 = cTFASTC_LDR_4x4_RGBA,
cTFATC_RGBA_INTERPOLATED_ALPHA = cTFATC_RGBA,
cTFASTC_4x4_RGBA = cTFASTC_LDR_4x4_RGBA
};
// For compressed texture formats, this returns the # of bytes per block. For uncompressed, it returns the # of bytes per pixel.
// NOTE: Previously, this function was called basis_get_bytes_per_block(), and it always returned 16*bytes_per_pixel for uncompressed formats which was confusing.
uint32_t basis_get_bytes_per_block_or_pixel(transcoder_texture_format fmt);
// Returns format's name in ASCII
// Returns the transcoder texture format's name in ASCII
const char* basis_get_format_name(transcoder_texture_format fmt);
// Returns basis texture format name in ASCII
const char* basis_get_tex_format_name(basis_tex_format fmt);
// Returns block format name in ASCII
const char* basis_get_block_format_name(block_format fmt);
@@ -143,6 +160,9 @@ namespace basist
// Returns true if the format is LDR.
inline bool basis_transcoder_format_is_ldr(transcoder_texture_format fmt) { return !basis_transcoder_format_is_hdr(fmt); }
// Returns true if the format is an LDR or HDR ASTC format.
bool basis_is_transcoder_texture_format_astc(transcoder_texture_format fmt);
// Returns the basisu::texture_format corresponding to the specified transcoder_texture_format.
basisu::texture_format basis_get_basisu_texture_format(transcoder_texture_format fmt);
@@ -156,23 +176,32 @@ namespace basist
uint32_t basis_get_uncompressed_bytes_per_pixel(transcoder_texture_format fmt);
// Returns the block width for the specified texture format, which is currently either 4 or 8 for FXT1.
uint32_t basis_get_block_width(transcoder_texture_format tex_type);
uint32_t basis_get_block_width(transcoder_texture_format fmt);
// Returns the block height for the specified texture format, which is currently always 4.
uint32_t basis_get_block_height(transcoder_texture_format tex_type);
uint32_t basis_get_block_height(transcoder_texture_format fmt);
// ASTC/XUASTC LDR formats only: Given a basis_tex_format (mode or codec), return the corresponding ASTC basisu::texture_format with the proper block size from 4x4-12x12.
basisu::texture_format basis_get_texture_format_from_xuastc_or_astc_ldr_basis_tex_format(basis_tex_format fmt);
// For any given basis_tex_format (mode or codec), return the LDR/HDR ASTC transcoder texture format with the proper block size.
transcoder_texture_format basis_get_transcoder_texture_format_from_basis_tex_format(basis_tex_format fmt);
// basis_get_transcoder_texture_format_from_xuastc_or_astc_ldr_basis_tex_format: same as basis_get_transcoder_texture_format_from_basis_tex_format (TODO: remove)
transcoder_texture_format basis_get_transcoder_texture_format_from_xuastc_or_astc_ldr_basis_tex_format(basis_tex_format fmt);
// Returns true if the specified format was enabled at compile time, and is supported for the specific basis/ktx2 texture format (ETC1S, UASTC, or UASTC HDR).
// Returns true if the specified format was enabled at compile time, and is supported for the specific basis/ktx2 texture format (ETC1S, UASTC, or UASTC HDR, or XUASTC LDR 4x4-12x12).
// For XUASTC the ASTC block size must match the transcoder_texture_format's ASTC block size.
bool basis_is_format_supported(transcoder_texture_format tex_type, basis_tex_format fmt = basis_tex_format::cETC1S);
// Returns the block width/height for the specified basis texture file format.
uint32_t basis_tex_format_get_block_width(basis_tex_format fmt);
uint32_t basis_tex_format_get_block_height(basis_tex_format fmt);
bool basis_tex_format_is_hdr(basis_tex_format fmt);
inline bool basis_tex_format_is_ldr(basis_tex_format fmt) { return !basis_tex_format_is_hdr(fmt); }
// Validates that the output buffer is large enough to hold the entire transcoded texture.
// For uncompressed texture formats, most input parameters are in pixels, not blocks. Blocks are 4x4 pixels.
// For uncompressed texture formats, most input parameters are in pixels, not blocks.
bool basis_validate_output_buffer_size(transcoder_texture_format target_format,
uint32_t output_blocks_buf_size_in_blocks_or_pixels,
uint32_t orig_width, uint32_t orig_height,
@@ -199,7 +228,7 @@ namespace basist
basisu::vector<block_preds> m_block_endpoint_preds[2];
enum { cMaxPrevFrameLevels = 16 };
basisu::vector<uint32_t> m_prev_frame_indices[2][cMaxPrevFrameLevels]; // [alpha_flag][level_index]
basisu::vector<uint32_t> m_prev_frame_indices[2][cMaxPrevFrameLevels]; // [alpha_flag][level_index]
void clear()
{
@@ -214,7 +243,46 @@ namespace basist
};
// Low-level helper classes that do the actual transcoding.
enum basisu_decode_flags
{
// PVRTC1: decode non-pow2 ETC1S texture level to the next larger power of 2 (not implemented yet, but we're going to support it). Ignored if the slice's dimensions are already a power of 2.
cDecodeFlagsPVRTCDecodeToNextPow2 = 2,
// When decoding to an opaque texture format, if the basis file has alpha, decode the alpha slice instead of the color slice to the output texture format.
// This is primarily to allow decoding of textures with alpha to multiple ETC1 textures (one for color, another for alpha).
cDecodeFlagsTranscodeAlphaDataToOpaqueFormats = 4,
// Forbid usage of BC1 3 color blocks (we don't support BC1 punchthrough alpha yet).
// This flag is used internally when decoding to BC3.
cDecodeFlagsBC1ForbidThreeColorBlocks = 8,
// The output buffer contains alpha endpoint/selector indices.
// Used internally when decoding formats like ASTC that require both color and alpha data to be available when transcoding to the output format.
cDecodeFlagsOutputHasAlphaIndices = 16,
// Enable slower, but higher quality transcoding for some formats.
// For ASTC/XUASTC->BC7, this enables partially analytical encoding vs. fully analytical.
cDecodeFlagsHighQuality = 32,
// Disable ETC1S->BC7 adaptive chroma filtering, for much faster transcoding to BC7.
cDecodeFlagsNoETC1SChromaFiltering = 64,
// Disable deblock filtering for XUASTC LDR transcoding to non-ASTC formats.
// For ASTC 8x6 or smaller block sizes, deblocking is always disabled unless you force it on using cDecodeFlagsForceDeblockFiltering.
cDecodeFlagsNoDeblockFiltering = 128,
// More aggressive deblock filtering (only used when it's enabled)
cDecodeFlagsStrongerDeblockFiltering = 256,
// Always apply deblocking, even for smaller ASTC block sizes (4x4-8x6).
cDecodeFlagsForceDeblockFiltering = 512,
// By default XUASTC LDR 4x4, 6x6 and 8x6 are directly transcoded to BC7 without always requiring a full ASTC block unpack and analytical BC7 encode. This is 1.4x up to 3x faster in WASM.
// This trade offs some quality. The largest transcoding speed gain is achieved when the source XUASTC data isn't dual plane and only uses 1 subset. Otherwise the actual perf. gain is variable.
// To disable this optimization for all XUASTC block sizes and always use the fallback encoder, specify cDecodeFlagXUASTCLDRDisableFastBC7Transcoding.
cDecodeFlagXUASTCLDRDisableFastBC7Transcoding = 1024
};
// ETC1S
class basisu_lowlevel_etc1s_transcoder
{
@@ -279,42 +347,20 @@ namespace basist
typedef basisu::vector<selector> selector_vec;
const selector_vec& get_selectors() const { return m_local_selectors; }
private:
const basisu_lowlevel_etc1s_transcoder* m_pGlobal_codebook;
endpoint_vec m_local_endpoints;
selector_vec m_local_selectors;
huffman_decoding_table m_endpoint_pred_model, m_delta_endpoint_model, m_selector_model, m_selector_history_buf_rle_model;
uint32_t m_selector_history_buf_size;
basisu_transcoder_state m_def_state;
};
enum basisu_decode_flags
{
// PVRTC1: decode non-pow2 ETC1S texture level to the next larger power of 2 (not implemented yet, but we're going to support it). Ignored if the slice's dimensions are already a power of 2.
cDecodeFlagsPVRTCDecodeToNextPow2 = 2,
// When decoding to an opaque texture format, if the basis file has alpha, decode the alpha slice instead of the color slice to the output texture format.
// This is primarily to allow decoding of textures with alpha to multiple ETC1 textures (one for color, another for alpha).
cDecodeFlagsTranscodeAlphaDataToOpaqueFormats = 4,
// Forbid usage of BC1 3 color blocks (we don't support BC1 punchthrough alpha yet).
// This flag is used internally when decoding to BC3.
cDecodeFlagsBC1ForbidThreeColorBlocks = 8,
// The output buffer contains alpha endpoint/selector indices.
// Used internally when decoding formats like ASTC that require both color and alpha data to be available when transcoding to the output format.
cDecodeFlagsOutputHasAlphaIndices = 16,
cDecodeFlagsHighQuality = 32,
cDecodeFlagsNoETC1SChromaFiltering = 64
};
// UASTC LDR 4x4
class basisu_lowlevel_uastc_ldr_4x4_transcoder
{
@@ -352,6 +398,76 @@ namespace basist
int channel0 = -1, int channel1 = -1);
};
#if BASISD_SUPPORT_XUASTC
// XUASTC LDR 4x4-12x12 or ASTC LDR 4x4-12x12
struct xuastc_decoded_image
{
uint32_t m_actual_block_width = 0, m_actual_block_height = 0, m_actual_width = 0, m_actual_height = 0;
bool m_actual_has_alpha = false, m_uses_srgb_astc_decode_mode = false;
bool decode(const uint8_t* pImage_data, uint32_t image_data_size,
astc_ldr_t::xuastc_decomp_image_init_callback_ptr pInit_callback, void* pInit_callback_data,
astc_ldr_t::xuastc_decomp_image_block_callback_ptr pBlock_callback, void* pBlock_callback_data)
{
const bool decomp_flag = astc_ldr_t::xuastc_ldr_decompress_image(pImage_data, image_data_size,
m_actual_block_width, m_actual_block_height,
m_actual_width, m_actual_height,
m_actual_has_alpha, m_uses_srgb_astc_decode_mode, basisu::g_debug_printf,
pInit_callback, pInit_callback_data,
pBlock_callback, pBlock_callback_data);
return decomp_flag;
}
void clear()
{
m_actual_block_width = 0;
m_actual_block_height = 0;
m_actual_width = 0;
m_actual_height = 0;
m_actual_has_alpha = false;
m_uses_srgb_astc_decode_mode = false;
}
};
#endif
class basisu_lowlevel_xuastc_ldr_transcoder
{
friend class basisu_transcoder;
public:
basisu_lowlevel_xuastc_ldr_transcoder();
bool transcode_slice(basis_tex_format src_format, bool use_astc_srgb_decode_profile, void* pDst_blocks, uint32_t src_num_blocks_x, uint32_t src_num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,
uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, bool has_alpha, const uint32_t orig_width, const uint32_t orig_height, uint32_t output_row_pitch_in_blocks_or_pixels = 0,
basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0);
bool transcode_slice(basis_tex_format src_format, bool use_astc_srgb_decode_profile, void* pDst_blocks, uint32_t src_num_blocks_x, uint32_t src_num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,
uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, const basis_file_header& header, const basis_slice_desc& slice_desc, uint32_t output_row_pitch_in_blocks_or_pixels = 0,
basisu_transcoder_state* pState = nullptr, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1, uint32_t decode_flags = 0)
{
return transcode_slice(src_format, use_astc_srgb_decode_profile, pDst_blocks, src_num_blocks_x, src_num_blocks_y, pImage_data, image_data_size, fmt,
output_block_or_pixel_stride_in_bytes, bc1_allow_threecolor_blocks, (header.m_flags & cBASISHeaderFlagHasAlphaSlices) != 0, slice_desc.m_orig_width, slice_desc.m_orig_height, output_row_pitch_in_blocks_or_pixels,
pState, output_rows_in_pixels, channel0, channel1, decode_flags);
}
// Container independent transcoding
bool transcode_image(
basis_tex_format src_format, bool use_astc_srgb_decode_profile,
transcoder_texture_format target_format,
void* pOutput_blocks, uint32_t output_blocks_buf_size_in_blocks_or_pixels,
const uint8_t* pCompressed_data, uint32_t compressed_data_length,
uint32_t src_num_blocks_x, uint32_t src_num_blocks_y, uint32_t orig_width, uint32_t orig_height, uint32_t level_index,
uint64_t slice_offset, uint32_t slice_length,
uint32_t decode_flags = 0,
bool has_alpha = false,
bool is_video = false,
uint32_t output_row_pitch_in_blocks_or_pixels = 0,
basisu_transcoder_state* pState = nullptr,
uint32_t output_rows_in_pixels = 0,
int channel0 = -1, int channel1 = -1);
};
// UASTC HDR 4x4
class basisu_lowlevel_uastc_hdr_4x4_transcoder
{
@@ -426,13 +542,13 @@ namespace basist
int channel0 = -1, int channel1 = -1);
};
// ASTC HDR 6x6 intermediate
class basisu_lowlevel_astc_hdr_6x6_intermediate_transcoder
// UASTC HDR 6x6 intermediate
class basisu_lowlevel_uastc_hdr_6x6_intermediate_transcoder
{
friend class basisu_transcoder;
public:
basisu_lowlevel_astc_hdr_6x6_intermediate_transcoder();
basisu_lowlevel_uastc_hdr_6x6_intermediate_transcoder();
bool transcode_slice(void* pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t* pImage_data, uint32_t image_data_size, block_format fmt,
uint32_t output_block_or_pixel_stride_in_bytes, bool bc1_allow_threecolor_blocks, bool has_alpha, const uint32_t orig_width, const uint32_t orig_height, uint32_t output_row_pitch_in_blocks_or_pixels = 0,
@@ -465,9 +581,11 @@ namespace basist
struct basisu_slice_info
{
// The image's ACTUAL dimensions in texels.
uint32_t m_orig_width;
uint32_t m_orig_height;
// The texture's dimensions in texels - always a multiple of the texture's underlying block size (4x4-12x12).
uint32_t m_width;
uint32_t m_height;
@@ -497,9 +615,11 @@ namespace basist
uint32_t m_image_index;
uint32_t m_total_levels;
// The image's ACTUAL dimensions in texels.
uint32_t m_orig_width;
uint32_t m_orig_height;
// The texture's dimensions in texels - always a multiple of the texture's underlying block size (4x4-12x12).
uint32_t m_width;
uint32_t m_height;
@@ -583,12 +703,13 @@ namespace basist
uint32_t m_block_height;
bool m_y_flipped; // true if the image was Y flipped
bool m_srgb; // true if the image is sRGB, false if linear
bool m_etc1s; // true if the file is ETC1S
bool m_has_alpha_slices; // true if the texture has alpha slices (for ETC1S: even slices RGB, odd slices alpha)
};
// High-level transcoder class which accepts .basis file data and allows the caller to query information about the file and transcode image levels to various texture formats.
// If you're just starting out this is the class you care about.
// If you're just starting out this is the class you care about (or see the KTX2 transcoder below).
class basisu_transcoder
{
basisu_transcoder(basisu_transcoder&);
@@ -639,11 +760,11 @@ namespace basist
// transcode_image_level() decodes a single mipmap level from the .basis file to any of the supported output texture formats.
// It'll first find the slice(s) to transcode, then call transcode_slice() one or two times to decode both the color and alpha texture data (or RG texture data from two slices for BC5).
// If the .basis file doesn't have alpha slices, the output alpha blocks will be set to fully opaque (all 255's).
// Currently, to decode to PVRTC1 the basis texture's dimensions in pixels must be a power of 2, due to PVRTC1 format requirements.
// Currently, to decode to PVRTC1 the basis texture's dimensions in pixels must be a power of 2, due to PVRTC1 format requirements.
// output_blocks_buf_size_in_blocks_or_pixels should be at least the image level's total_blocks (num_blocks_x * num_blocks_y), or the total number of output pixels if fmt==cTFRGBA32 etc.
// output_row_pitch_in_blocks_or_pixels: Number of blocks or pixels per row. If 0, the transcoder uses the slice's num_blocks_x or orig_width (NOT num_blocks_x * 4). Ignored for PVRTC1 (due to texture swizzling).
// output_rows_in_pixels: Ignored unless fmt is uncompressed (cRGBA32, etc.). The total number of output rows in the output buffer. If 0, the transcoder assumes the slice's orig_height (NOT num_blocks_y * 4).
// Notes:
// Notes:
// - basisu_transcoder_init() must have been called first to initialize the transcoder lookup tables before calling this function.
// - This method assumes the output texture buffer is readable. In some cases to handle alpha, the transcoder will write temporary data to the output texture in
// a first pass, which will be read in a second pass.
@@ -682,15 +803,16 @@ namespace basist
const basisu_lowlevel_etc1s_transcoder& get_lowlevel_etc1s_decoder() const { return m_lowlevel_etc1s_decoder; }
basisu_lowlevel_etc1s_transcoder& get_lowlevel_etc1s_decoder() { return m_lowlevel_etc1s_decoder; }
const basisu_lowlevel_uastc_ldr_4x4_transcoder& get_lowlevel_uastc_decoder() const { return m_lowlevel_uastc_decoder; }
basisu_lowlevel_uastc_ldr_4x4_transcoder& get_lowlevel_uastc_decoder() { return m_lowlevel_uastc_decoder; }
const basisu_lowlevel_uastc_ldr_4x4_transcoder& get_lowlevel_uastc_decoder() const { return m_lowlevel_uastc_ldr_4x4_decoder; }
basisu_lowlevel_uastc_ldr_4x4_transcoder& get_lowlevel_uastc_decoder() { return m_lowlevel_uastc_ldr_4x4_decoder; }
private:
mutable basisu_lowlevel_etc1s_transcoder m_lowlevel_etc1s_decoder;
mutable basisu_lowlevel_uastc_ldr_4x4_transcoder m_lowlevel_uastc_decoder;
mutable basisu_lowlevel_uastc_ldr_4x4_transcoder m_lowlevel_uastc_ldr_4x4_decoder;
mutable basisu_lowlevel_xuastc_ldr_transcoder m_lowlevel_xuastc_ldr_decoder;
mutable basisu_lowlevel_uastc_hdr_4x4_transcoder m_lowlevel_uastc_4x4_hdr_decoder;
mutable basisu_lowlevel_astc_hdr_6x6_transcoder m_lowlevel_astc_6x6_hdr_decoder;
mutable basisu_lowlevel_astc_hdr_6x6_intermediate_transcoder m_lowlevel_astc_6x6_hdr_intermediate_decoder;
mutable basisu_lowlevel_uastc_hdr_6x6_intermediate_transcoder m_lowlevel_astc_6x6_hdr_intermediate_decoder;
bool m_ready_to_transcode;
@@ -701,7 +823,7 @@ namespace basist
// basisu_transcoder_init() MUST be called before a .basis file can be transcoded.
void basisu_transcoder_init();
enum debug_flags_t
{
cDebugFlagVisCRs = 1,
@@ -711,10 +833,10 @@ namespace basist
uint32_t get_debug_flags();
void set_debug_flags(uint32_t f);
// ------------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------------
// Optional .KTX2 file format support
// KTX2 reading optionally requires miniz or Zstd decompressors for supercompressed UASTC files.
// ------------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------------
#if BASISD_SUPPORT_KTX2
#pragma pack(push)
#pragma pack(1)
@@ -764,10 +886,10 @@ namespace basist
basisu::packed_uint<4> m_alpha_slice_byte_length;
};
struct ktx2_astc_hdr_6x6_intermediate_image_desc
struct ktx2_slice_offset_len_desc
{
basisu::packed_uint<4> m_rgb_slice_byte_offset;
basisu::packed_uint<4> m_rgb_slice_byte_length;
basisu::packed_uint<4> m_slice_byte_offset;
basisu::packed_uint<4> m_slice_byte_length;
};
struct ktx2_animdata
@@ -779,7 +901,7 @@ namespace basist
#pragma pack(pop)
const uint32_t KTX2_VK_FORMAT_UNDEFINED = 0;
// These are standard Vulkan texture VkFormat ID's, see https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkFormat.html
const uint32_t KTX2_FORMAT_ASTC_4x4_SFLOAT_BLOCK = 1000066000;
const uint32_t KTX2_FORMAT_ASTC_5x4_SFLOAT_BLOCK = 1000066001;
@@ -789,12 +911,28 @@ namespace basist
const uint32_t KTX2_FORMAT_ASTC_8x5_SFLOAT_BLOCK = 1000066005;
const uint32_t KTX2_FORMAT_ASTC_8x6_SFLOAT_BLOCK = 1000066006;
const uint32_t KTX2_FORMAT_ASTC_4x4_UNORM_BLOCK = 157, KTX2_FORMAT_ASTC_4x4_SRGB_BLOCK = 158;
const uint32_t KTX2_FORMAT_ASTC_5x4_UNORM_BLOCK = 159, KTX2_FORMAT_ASTC_5x4_SRGB_BLOCK = 160;
const uint32_t KTX2_FORMAT_ASTC_5x5_UNORM_BLOCK = 161, KTX2_FORMAT_ASTC_5x5_SRGB_BLOCK = 162;
const uint32_t KTX2_FORMAT_ASTC_6x5_UNORM_BLOCK = 163, KTX2_FORMAT_ASTC_6x5_SRGB_BLOCK = 164;
const uint32_t KTX2_FORMAT_ASTC_6x6_UNORM_BLOCK = 165, KTX2_FORMAT_ASTC_6x6_SRGB_BLOCK = 166;
const uint32_t KTX2_FORMAT_ASTC_8x5_UNORM_BLOCK = 167, KTX2_FORMAT_ASTC_8x5_SRGB_BLOCK = 168;
const uint32_t KTX2_FORMAT_ASTC_8x6_UNORM_BLOCK = 169, KTX2_FORMAT_ASTC_8x6_SRGB_BLOCK = 170;
const uint32_t KTX2_FORMAT_ASTC_10x5_UNORM_BLOCK = 173, KTX2_FORMAT_ASTC_10x5_SRGB_BLOCK = 174;
const uint32_t KTX2_FORMAT_ASTC_10x6_UNORM_BLOCK = 175, KTX2_FORMAT_ASTC_10x6_SRGB_BLOCK = 176;
const uint32_t KTX2_FORMAT_ASTC_8x8_UNORM_BLOCK = 171, KTX2_FORMAT_ASTC_8x8_SRGB_BLOCK = 172; // note the ASTC block size order is off in the vkFormat definitions
const uint32_t KTX2_FORMAT_ASTC_10x8_UNORM_BLOCK = 177, KTX2_FORMAT_ASTC_10x8_SRGB_BLOCK = 178;
const uint32_t KTX2_FORMAT_ASTC_10x10_UNORM_BLOCK = 179, KTX2_FORMAT_ASTC_10x10_SRGB_BLOCK = 180;
const uint32_t KTX2_FORMAT_ASTC_12x10_UNORM_BLOCK = 181, KTX2_FORMAT_ASTC_12x10_SRGB_BLOCK = 182;
const uint32_t KTX2_FORMAT_ASTC_12x12_UNORM_BLOCK = 183, KTX2_FORMAT_ASTC_12x12_SRGB_BLOCK = 184;
const uint32_t KTX2_KDF_DF_MODEL_ASTC = 162; // 0xA2
const uint32_t KTX2_KDF_DF_MODEL_ETC1S = 163; // 0xA3
const uint32_t KTX2_KDF_DF_MODEL_UASTC_LDR_4X4 = 166; // 0xA6
const uint32_t KTX2_KDF_DF_MODEL_UASTC_HDR_4X4 = 167; // 0xA7
const uint32_t KTX2_KDF_DF_MODEL_ASTC_HDR_6X6_INTERMEDIATE = 168; // 0xA8, TODO - coordinate with Khronos on this
const uint32_t KTX2_KDF_DF_MODEL_UASTC_HDR_6X6_INTERMEDIATE = 168; // 0xA8, TODO - coordinate with Khronos on this
const uint32_t KTX2_KDF_DF_MODEL_XUASTC_LDR_INTERMEDIATE = 169; // 0xA9, TODO - coordinate with Khronos on this
const uint32_t KTX2_IMAGE_IS_P_FRAME = 2;
const uint32_t KTX2_UASTC_BLOCK_SIZE = 16; // also the block size for UASTC_HDR
const uint32_t KTX2_MAX_SUPPORTED_LEVEL_COUNT = 16; // this is an implementation specific constraint and can be increased
@@ -878,12 +1016,12 @@ namespace basist
{
case KTX2_DF_PRIMARIES_UNSPECIFIED: return "UNSPECIFIED";
case KTX2_DF_PRIMARIES_BT709: return "BT709";
case KTX2_DF_PRIMARIES_BT601_EBU: return "EBU";
case KTX2_DF_PRIMARIES_BT601_EBU: return "EBU";
case KTX2_DF_PRIMARIES_BT601_SMPTE: return "SMPTE";
case KTX2_DF_PRIMARIES_BT2020: return "BT2020";
case KTX2_DF_PRIMARIES_CIEXYZ: return "CIEXYZ";
case KTX2_DF_PRIMARIES_ACES: return "ACES";
case KTX2_DF_PRIMARIES_ACESCC: return "ACESCC";
case KTX2_DF_PRIMARIES_ACESCC: return "ACESCC";
case KTX2_DF_PRIMARIES_NTSC1953: return "NTSC1953";
case KTX2_DF_PRIMARIES_PAL525: return "PAL525";
case KTX2_DF_PRIMARIES_DISPLAYP3: return "DISPLAYP3";
@@ -891,7 +1029,7 @@ namespace basist
default: break;
}
return "?";
}
}
// Information about a single 2D texture "image" in a KTX2 file.
struct ktx2_image_level_info
@@ -901,19 +1039,19 @@ namespace basist
uint32_t m_layer_index;
uint32_t m_face_index;
// The image's actual (or the original source image's) width/height in pixels, which may not be divisible by 4 pixels.
// The image's ACTUAL (or the original source image's) width/height in pixels, which may not be divisible by the block size (4-12 pixels).
uint32_t m_orig_width;
uint32_t m_orig_height;
// The image's physical width/height, which will always be divisible by 4 pixels.
// The image's physical width/height, which will always be divisible by the format's block size (4-12 pixels).
uint32_t m_width;
uint32_t m_height;
// The texture's dimensions in 4x4 or 6x6 texel blocks.
// The texture's dimensions in 4x4-12x12 texel blocks.
uint32_t m_num_blocks_x;
uint32_t m_num_blocks_y;
// The format's block width/height (currently either 4 or 6).
// The format's block width/height (4-12).
uint32_t m_block_width;
uint32_t m_block_height;
@@ -926,7 +1064,7 @@ namespace basist
// true if the image is an I-Frame. Currently, for ETC1S textures, the first frame will always be an I-Frame, and subsequent frames will always be P-Frames.
bool m_iframe_flag;
};
// Thread-specific ETC1S/supercompressed UASTC transcoder state. (If you're not doing multithreading transcoding you can ignore this.)
struct ktx2_transcoder_state
{
@@ -944,9 +1082,9 @@ namespace basist
// This class is quite similar to basisu_transcoder. It treats KTX2 files as a simple container for ETC1S/UASTC texture data.
// It does not support 1D or 3D textures.
// It only supports 2D and cubemap textures, with or without mipmaps, texture arrays of 2D/cubemap textures, and texture video files.
// It only supports 2D and cubemap textures, with or without mipmaps, texture arrays of 2D/cubemap textures, and texture video files.
// It only supports raw non-supercompressed UASTC, ETC1S, UASTC+Zstd, or UASTC+zlib compressed files.
// DFD (Data Format Descriptor) parsing is purposely as simple as possible.
// DFD (Data Format Descriptor) parsing is purposely as simple as possible.
// If you need to know how to interpret the texture channels you'll need to parse the DFD yourself after calling get_dfd().
class ktx2_transcoder
{
@@ -971,10 +1109,10 @@ namespace basist
// Returns the KTX2 level index array. There will be one entry for each mipmap level. Valid after init().
const basisu::vector<ktx2_level_index>& get_level_index() const { return m_levels; }
// Returns the texture's width in texels. Always non-zero, might not be divisible by 4. Valid after init().
// Returns the texture's width in texels. Always non-zero, might not be divisible by the block size. Valid after init().
uint32_t get_width() const { return m_header.m_pixel_width; }
// Returns the texture's height in texels. Always non-zero, might not be divisible by 4. Valid after init().
// Returns the texture's height in texels. Always non-zero, might not be divisible by the block size. Valid after init().
uint32_t get_height() const { return m_header.m_pixel_height; }
// Returns the texture's number of mipmap levels. Always returns 1 or higher. Valid after init().
@@ -986,15 +1124,15 @@ namespace basist
// Returns 0 or the number of layers in the texture array or texture video. Valid after init().
uint32_t get_layers() const { return m_header.m_layer_count; }
// Returns cETC1S, cUASTC4x4, cUASTC_HDR_4x4, cASTC_HDR_6x6, cASTC_HDR_6x6_INTERMEDIATE. Valid after init().
// Returns cETC1S, cUASTC4x4, cUASTC_HDR_4x4, cASTC_HDR_6x6, cUASTC_HDR_6x6_INTERMEDIATE, etc. Valid after init().
basist::basis_tex_format get_basis_tex_format() const { return m_format; }
// ETC1S LDR 4x4
bool is_etc1s() const { return get_basis_tex_format() == basist::basis_tex_format::cETC1S; }
// UASTC LDR 4x4 (only)
bool is_uastc() const { return get_basis_tex_format() == basist::basis_tex_format::cUASTC4x4; }
bool is_uastc() const { return get_basis_tex_format() == basist::basis_tex_format::cUASTC_LDR_4x4; }
// Is ASTC HDR 4x4 or 6x6
bool is_hdr() const
{
@@ -1006,18 +1144,26 @@ namespace basist
return !is_hdr();
}
// is UASTC HDR 4x4 (which is also standard ASTC HDR 4x4 data)
bool is_hdr_4x4() const
{
return (get_basis_tex_format() == basist::basis_tex_format::cUASTC_HDR_4x4);
}
// is ASTC HDR 6x6 or UASTC HDR 6x6 intermediate (only)
bool is_hdr_6x6() const
{
return (get_basis_tex_format() == basist::basis_tex_format::cASTC_HDR_6x6) || (get_basis_tex_format() == basist::basis_tex_format::cASTC_HDR_6x6_INTERMEDIATE);
return (get_basis_tex_format() == basist::basis_tex_format::cASTC_HDR_6x6) || (get_basis_tex_format() == basist::basis_tex_format::cUASTC_HDR_6x6_INTERMEDIATE);
}
// is ASTC LDR 4x4-12x12 (only)
bool is_astc_ldr() const { return basis_tex_format_is_astc_ldr(get_basis_tex_format()); }
// is XUASTC LDR 4x4-12x12 (only)
bool is_xuastc_ldr() const { return basis_tex_format_is_xuastc_ldr(get_basis_tex_format()); }
uint32_t get_block_width() const { return basis_tex_format_get_block_width(get_basis_tex_format()); }
uint32_t get_block_height() const { return basis_tex_format_get_block_height(get_basis_tex_format()); }
uint32_t get_block_height() const { return basis_tex_format_get_block_height(get_basis_tex_format()); }
// Returns true if the ETC1S file has two planes (typically RGBA, or RRRG), or true if the UASTC file has alpha data. Valid after init().
uint32_t get_has_alpha() const { return m_has_alpha; }
@@ -1032,17 +1178,19 @@ namespace basist
// Returns the DFD color primary.
// We do not validate the color primaries, so the returned value may not be in the ktx2_df_color_primaries enum.
ktx2_df_color_primaries get_dfd_color_primaries() const { return m_dfd_color_prims; }
// Returns KTX2_KHR_DF_TRANSFER_LINEAR or KTX2_KHR_DF_TRANSFER_SRGB.
uint32_t get_dfd_transfer_func() const { return m_dfd_transfer_func; }
bool is_srgb() const { return (get_dfd_transfer_func() == KTX2_KHR_DF_TRANSFER_SRGB); }
uint32_t get_dfd_flags() const { return m_dfd_flags; }
// Returns 1 (ETC1S/UASTC) or 2 (ETC1S with an internal alpha channel).
uint32_t get_dfd_total_samples() const { return m_dfd_samples; }
// Returns the channel mapping for each DFD "sample". UASTC always has 1 sample, ETC1S can have one or two.
// Note the returned value SHOULD be one of the ktx2_df_channel_id enums, but we don't validate that.
// Returns the channel mapping for each DFD "sample". UASTC always has 1 sample, ETC1S can have one or two.
// Note the returned value SHOULD be one of the ktx2_df_channel_id enums, but we don't validate that.
// It's up to the caller to decide what to do if the value isn't in the enum.
ktx2_df_channel_id get_dfd_channel_id0() const { return m_dfd_chan0; }
ktx2_df_channel_id get_dfd_channel_id1() const { return m_dfd_chan1; }
@@ -1050,11 +1198,11 @@ namespace basist
// Key value field data.
struct key_value
{
// The key field is UTF8 and always zero terminated.
// The key field is UTF8 and always zero terminated.
// In memory we always append a zero terminator to the key.
basisu::uint8_vec m_key;
// The value may be empty. In the KTX2 file it consists of raw bytes which may or may not be zero terminated.
// The value may be empty. In the KTX2 file it consists of raw bytes which may or may not be zero terminated.
// In memory we always append a zero terminator to the value.
basisu::uint8_vec m_value;
@@ -1076,7 +1224,7 @@ namespace basist
// Returns the array of ETC1S image descriptors, which is only valid after get_etc1s_image_descs() is called.
const basisu::vector<ktx2_etc1s_image_desc>& get_etc1s_image_descs() const { return m_etc1s_image_descs; }
const basisu::vector<ktx2_astc_hdr_6x6_intermediate_image_desc>& get_astc_hdr_6x6_intermediate_image_descs() const { return m_astc_6x6_intermediate_image_descs; }
const basisu::vector<ktx2_slice_offset_len_desc>& get_slice_offset_len_descs() const { return m_slice_offset_len_descs; }
// Must have called startTranscoding() first
uint32_t get_etc1s_image_descs_image_flags(uint32_t level_index, uint32_t layer_index, uint32_t face_index) const;
@@ -1084,21 +1232,21 @@ namespace basist
// is_video() is only valid after start_transcoding() is called.
// For ETC1S data, if this returns true you must currently transcode the file from first to last frame, in order, without skipping any frames.
bool is_video() const { return m_is_video; }
// Defaults to 0, only non-zero if the key existed in the source KTX2 file.
float get_ldr_hdr_upconversion_nit_multiplier() const { return m_ldr_hdr_upconversion_nit_multiplier; }
// start_transcoding() MUST be called before calling transcode_image().
// start_transcoding() MUST be called before calling transcode_image_level().
// This method decompresses the ETC1S global endpoint/selector codebooks, which is not free, so try to avoid calling it excessively.
bool start_transcoding();
// get_image_level_info() be called after init(), but the m_iframe_flag's won't be valid until start_transcoding() is called.
// You can call this method before calling transcode_image_level() to retrieve basic information about the mipmap level's dimensions, etc.
bool get_image_level_info(ktx2_image_level_info& level_info, uint32_t level_index, uint32_t layer_index, uint32_t face_index) const;
// transcode_image_level() transcodes a single 2D texture or cubemap face from the KTX2 file.
// Internally it uses the same low-level transcode API's as basisu_transcoder::transcode_image_level().
// If the file is UASTC and is supercompressed with Zstandard, and the file is a texture array or cubemap, it's highly recommended that each mipmap level is
// If the file is UASTC and is supercompressed with Zstandard, and the file is a texture array or cubemap, it's highly recommended that each mipmap level is
// completely transcoded before switching to another level. Every time the mipmap level is changed all supercompressed level data must be decompressed using Zstandard as a single unit.
// Currently ETC1S videos must always be transcoded from first to last frame (or KTX2 "layer"), in order, with no skipping of frames.
// By default this method is not thread safe unless you specify a pointer to a user allocated thread-specific transcoder_state struct.
@@ -1108,7 +1256,7 @@ namespace basist
basist::transcoder_texture_format fmt,
uint32_t decode_flags = 0, uint32_t output_row_pitch_in_blocks_or_pixels = 0, uint32_t output_rows_in_pixels = 0, int channel0 = -1, int channel1 = -1,
ktx2_transcoder_state *pState = nullptr);
private:
const uint8_t* m_pData;
uint32_t m_data_size;
@@ -1117,26 +1265,30 @@ namespace basist
basisu::vector<ktx2_level_index> m_levels;
basisu::uint8_vec m_dfd;
key_value_vec m_key_values;
ktx2_etc1s_global_data_header m_etc1s_header;
basisu::vector<ktx2_etc1s_image_desc> m_etc1s_image_descs;
basisu::vector<ktx2_astc_hdr_6x6_intermediate_image_desc> m_astc_6x6_intermediate_image_descs;
basisu::vector<ktx2_slice_offset_len_desc> m_slice_offset_len_descs;
basist::basis_tex_format m_format;
uint32_t m_dfd_color_model;
ktx2_df_color_primaries m_dfd_color_prims;
uint32_t m_dfd_transfer_func;
// KTX2_KHR_DF_TRANSFER_LINEAR vs. KTX2_KHR_DF_TRANSFER_SRGB (for XUASTC LDR: which profile was used during encoding)
uint32_t m_dfd_transfer_func;
uint32_t m_dfd_flags;
uint32_t m_dfd_samples;
ktx2_df_channel_id m_dfd_chan0, m_dfd_chan1;
basist::basisu_lowlevel_etc1s_transcoder m_etc1s_transcoder;
basist::basisu_lowlevel_uastc_ldr_4x4_transcoder m_uastc_transcoder;
basist::basisu_lowlevel_uastc_ldr_4x4_transcoder m_uastc_ldr_transcoder;
basist::basisu_lowlevel_xuastc_ldr_transcoder m_xuastc_ldr_transcoder;
basist::basisu_lowlevel_uastc_hdr_4x4_transcoder m_uastc_hdr_transcoder;
basist::basisu_lowlevel_astc_hdr_6x6_transcoder m_astc_hdr_6x6_transcoder;
basist::basisu_lowlevel_astc_hdr_6x6_intermediate_transcoder m_astc_hdr_6x6_intermediate_transcoder;
basist::basisu_lowlevel_uastc_hdr_6x6_intermediate_transcoder m_astc_hdr_6x6_intermediate_transcoder;
ktx2_transcoder_state m_def_transcoder_state;
bool m_has_alpha;
@@ -1144,7 +1296,7 @@ namespace basist
float m_ldr_hdr_upconversion_nit_multiplier;
bool decompress_level_data(uint32_t level_index, basisu::uint8_vec& uncomp_data);
bool read_astc_6x6_hdr_intermediate_global_data();
bool read_slice_offset_len_global_data();
bool decompress_etc1s_global_data();
bool read_key_values();
};
@@ -1165,7 +1317,7 @@ namespace basist
break;
}
}
if (!p)
p = key_values.enlarge(1);
@@ -1189,3 +1341,4 @@ namespace basist
bool basisu_transcoder_supports_ktx2_zstd();
} // namespace basisu

File diff suppressed because it is too large Load Diff