| // bc7e_scalar.cpp - Pure scalar C++17 port of bc7e.ispc (auto-derived, then |
| // hand-fixed). De-SIMD'd: the single SPMD foreach over blocks becomes a plain |
| // for loop; uniform/varying are stripped (one lane/block, no gang), and the |
| // ISPC stdlib surface is provided by ispc_compat.h. Encodes one 4x4 BC7 block |
| // per iteration. Logic/math and the public API are preserved verbatim. |
| #include "basisu_bc7e_scalar.h" |
| #include <cstdint> |
| #include <cstring> // memset, memcpy |
| #include <cassert> |
| #include <cmath> |
| #include <cstdio> |
| |
| // --- ISPC compatibility shim (formerly ispc_compat.h, inlined here so this .cpp |
| // --- plus basisu_bc7e_scalar.h are fully self-contained). Supplies the small ISPC |
| // --- stdlib surface the de-SIMD'd code uses, the base integer type aliases, and |
| // --- the coherent control-flow keyword macros. Kept in the .cpp (not the public |
| // --- header) so these global templates/macros don't leak into the rest of the project. |
| |
| // ISPC base integer type aliases (the fixed-width *_t names come from <cstdint>). |
| typedef int8_t int8; |
| typedef int16_t int16; |
| typedef int32_t int32; |
| typedef int64_t int64; |
| |
| // ISPC "coherent" control-flow keywords -> plain C++ control flow (no-op at gang width 1). |
| #define cif if |
| #define cfor for |
| #define cwhile while |
| |
| // Mark an intentionally-unused variable/parameter (silences C4100/C4189). |
| #define NOTE_UNUSED(x) (void)(x) |
| |
| // Range-checked cast for a float that is known to hold a small non-negative integral |
| // value (e.g. an 8-bit channel min/max). Asserts the value is in [0,255] in debug, then |
| // narrows to uint32_t. Used at the few float->uint sites so the truncation is explicit |
| // and guarded rather than an implicit (and portability-warning-triggering) conversion. |
| static inline uint32_t float_to_uint8(float v) { assert((v >= 0.0f) && (v <= 255.0f)); return (uint32_t)v; } |
| |
| // ISPC stdlib scalar equivalents. min/max match minss/maxss semantics (the second |
| // operand is returned on an unordered/NaN comparison), and clamp() is defined as |
| // min(max(v,lo),hi) -- NOT a direct ternary -- so clamp(NaN,lo,hi) yields lo just as |
| // it does under ISPC (a direct ternary would yield NaN, which casts to INT_MIN and |
| // becomes an out-of-bounds index). |
| template <class T> static inline T min(T a, T b) { return a < b ? a : b; } |
| template <class T> static inline T max(T a, T b) { return a > b ? a : b; } |
| template <class T> static inline T clamp(T v, T lo, T hi) { return min(max(v, lo), hi); } |
| template <class T> static inline T select(bool c, T a, T b) { return c ? a : b; } |
| template <class T> static inline bool all(T v) { return (bool)v; } |
| template <class T> static inline bool any(T v) { return (bool)v; } |
| template <class T> static inline bool none(T v) { return !(bool)v; } |
| template <class T> static inline T abs(T v) { return v < 0 ? -v : v; } |
| // floor()/sqrt() on floats resolve to the global float overloads <cmath> provides under MSVC. |
| |
| namespace bc7e_scalar { |
| |
| // TEMP debug checkpoint (single-threaded runs only). Prints the source line so |
| // the last value before a crash pinpoints the failing statement. Remove once verified. |
| #define HERE() do { fprintf(stderr, "L%d\n", __LINE__); fflush(stderr); } while(0) |
| |
| // Tiny bias added to data-dependent denominators that can legitimately be zero |
| // (flat blocks / flat channels -> zero endpoint deltas). The original SIMD code |
| // let the divide produce +/-Inf/NaN and relied on a later min/max clamp to |
| // sanitize it; in scalar code an unguarded 0/0 NaN can become an INT_MIN array |
| // index. Biasing keeps the divisor finite and nonzero with negligible effect on |
| // the result (denominators are otherwise >= 1 in these spots). |
| static const float BC7E_DENOM_BIAS = 0.0000125f; |
| |
| // bc7e.ispc - Fast high quality SIMD BC7 encoder |
| // Copyright (C) 2018-2020 Binomial LLC, All rights reserved. Apache 2.0 license - see LICENSE. |
| // Typically compiled as: ispc -g -O2 "%(Filename).ispc" -o "$(TargetDir)%(Filename).obj" -h "$(ProjectDir)%(Filename)_ispc.h" --target=sse2,sse4,avx,avx2 --opt=fast-math --opt=disable-assertions |
| // --opt=fast-math is optional (doesn't make much if any measurable difference). |
| // Thanks to ArasP for the determinism fix. |
| |
| #define BC7E_NON_DETERMINISTIC (0) |
| #define BC7E_2SUBSET_CHECKERBOARD_PARTITION_INDEX (34) |
| #define BC7E_BLOCK_SIZE (16) |
| #define BC7E_MAX_PARTITIONS0 (16) |
| #define BC7E_MAX_PARTITIONS1 (64) |
| #define BC7E_MAX_PARTITIONS2 (64) |
| #define BC7E_MAX_PARTITIONS3 (64) |
| #define BC7E_MAX_PARTITIONS7 (64) |
| #define BC7E_MAX_UBER_LEVEL (4) |
| |
| // endpoint_err::m_error is a real uint16_t (was a 64-bit alias inherited from the ISPC source). |
| // Verified safe to narrow: all stores are single-channel squared errors (k-c)^2 <= 65025, |
| // guarded by safe_cast_uint16(); reads only compare or sum 3-4 of them (integer-promoted to |
| // int, max ~260100), so 16 bits is sufficient and no 64-bit width is relied upon. |
| |
| #ifndef UINT16_MAX |
| #define UINT16_MAX (0xFFFF) |
| #endif |
| |
| #ifndef UINT_MAX |
| #define UINT_MAX (0xFFFFFFFFU) |
| #endif |
| |
| #ifndef UINT64_MAX |
| #define UINT64_MAX (0xFFFFFFFFFFFFFFFFULL) |
| #endif |
| |
| #ifndef INT64_MAX |
| #define INT64_MAX (0x7FFFFFFFFFFFFFFFULL) |
| #endif |
| |
| |
| static inline int32_t clampi( int32_t value, int32_t low, int32_t high) { return clamp(value, low, high); } |
| [[maybe_unused]] static inline uint32_t clampu( uint32_t value, uint32_t low, uint32_t high) { return clamp(value, low, high); } |
| static inline float clampf( float value, float low, float high) { return clamp(value, low, high); } |
| |
| static inline float saturate( float value) { return clampf(value, 0, 1.0f); } |
| [[maybe_unused]] static inline float saturate255( float value) { return clampf(value, 0, 255.0f); } |
| |
| [[maybe_unused]] static inline uint8_t minimumub( uint8_t a, uint8_t b) { return min(a, b); } |
| static inline int32_t minimumi( int32_t a, int32_t b) { return min(a, b); } |
| static inline uint32_t minimumu( uint32_t a, uint32_t b) { return min(a, b); } |
| [[maybe_unused]] static inline uint64_t minimumu64( uint64_t a, uint64_t b) { return min(a, b); } |
| static inline float minimumf( float a, float b) { return min(a, b); } |
| |
| [[maybe_unused]] static inline uint8_t maximumub( uint8_t a, uint8_t b) { return max(a, b); } |
| static inline int32_t maximumi( int32_t a, int32_t b) { return max(a, b); } |
| static inline uint32_t maximumu( uint32_t a, uint32_t b) { return max(a, b); } |
| static inline float maximumf( float a, float b) { return max(a, b); } |
| |
| static inline int32_t iabs32( int32_t v) { uint32_t msk = v >> 31; return (v ^ msk) - msk; } |
| |
| [[maybe_unused]] static inline void swapub( uint8_t * a, uint8_t * b) { uint8_t t = *a; *a = *b; *b = t; } |
| static inline void swapu( uint32_t * a, uint32_t * b) { uint32_t t = *a; *a = *b; *b = t; } |
| static inline void swapi( int32_t * a, int32_t * b) { int32_t t = *a; *a = *b; *b = t; } |
| static inline void swapf( float * a, float * b) { float t = *a; *a = *b; *b = t; } |
| |
| static inline float square(float s) { return s * s; } |
| [[maybe_unused]] static inline int square(int s) { return s * s; } |
| |
| struct color_quad_u8 |
| { |
| uint8_t m_c[4]; |
| }; |
| |
| struct color_quad_i |
| { |
| int32_t m_c[4]; |
| }; |
| |
| struct color_quad_f |
| { |
| float m_c[4]; |
| }; |
| |
| [[maybe_unused]] static inline color_quad_i component_min_rgb(const color_quad_i * pA, const color_quad_i * pB) |
| { |
| color_quad_i res; |
| res.m_c[0] = minimumi(pA->m_c[0], pB->m_c[0]); |
| res.m_c[1] = minimumi(pA->m_c[1], pB->m_c[1]); |
| res.m_c[2] = minimumi(pA->m_c[2], pB->m_c[2]); |
| res.m_c[3] = 255; |
| return res; |
| } |
| |
| [[maybe_unused]] static inline color_quad_i component_max_rgb(const color_quad_i * pA, const color_quad_i * pB) |
| { |
| color_quad_i res; |
| res.m_c[0] = maximumi(pA->m_c[0], pB->m_c[0]); |
| res.m_c[1] = maximumi(pA->m_c[1], pB->m_c[1]); |
| res.m_c[2] = maximumi(pA->m_c[2], pB->m_c[2]); |
| res.m_c[3] = 255; |
| return res; |
| } |
| |
| static inline color_quad_i *color_quad_i_set_clamped( color_quad_i * pRes, int32_t r, int32_t g, int32_t b, int32_t a) |
| { |
| pRes->m_c[0] = clampi(r, 0, 255); |
| pRes->m_c[1] = clampi(g, 0, 255); |
| pRes->m_c[2] = clampi(b, 0, 255); |
| pRes->m_c[3] = clampi(a, 0, 255); |
| return pRes; |
| } |
| |
| static inline color_quad_i *color_quad_i_set( color_quad_i * pRes, int32_t r, int32_t g, int32_t b, int32_t a) |
| { |
| pRes->m_c[0] = r; |
| pRes->m_c[1] = g; |
| pRes->m_c[2] = b; |
| pRes->m_c[3] = a; |
| return pRes; |
| } |
| |
| static inline bool color_quad_i_equals(const color_quad_i * pLHS, const color_quad_i * pRHS) |
| { |
| return (pLHS->m_c[0] == pRHS->m_c[0]) && (pLHS->m_c[1] == pRHS->m_c[1]) && (pLHS->m_c[2] == pRHS->m_c[2]) && (pLHS->m_c[3] == pRHS->m_c[3]); |
| } |
| |
| static inline bool color_quad_i_notequals(const color_quad_i * pLHS, const color_quad_i * pRHS) |
| { |
| return !color_quad_i_equals(pLHS, pRHS); |
| } |
| |
| struct vec4F |
| { |
| float m_c[4]; |
| }; |
| |
| static inline vec4F * vec4F_set_scalar( vec4F * pV, float x) |
| { |
| pV->m_c[0] = x; |
| pV->m_c[1] = x; |
| pV->m_c[2] = x; |
| pV->m_c[3] = x; |
| return pV; |
| } |
| |
| static inline vec4F * vec4F_set( vec4F * pV, float x, float y, float z, float w) |
| { |
| pV->m_c[0] = x; |
| pV->m_c[1] = y; |
| pV->m_c[2] = z; |
| pV->m_c[3] = w; |
| return pV; |
| } |
| |
| static inline vec4F * vec4F_saturate_in_place( vec4F * pV) |
| { |
| pV->m_c[0] = saturate(pV->m_c[0]); |
| pV->m_c[1] = saturate(pV->m_c[1]); |
| pV->m_c[2] = saturate(pV->m_c[2]); |
| pV->m_c[3] = saturate(pV->m_c[3]); |
| return pV; |
| } |
| |
| static inline vec4F vec4F_saturate(const vec4F * pV) |
| { |
| vec4F res; |
| res.m_c[0] = saturate(pV->m_c[0]); |
| res.m_c[1] = saturate(pV->m_c[1]); |
| res.m_c[2] = saturate(pV->m_c[2]); |
| res.m_c[3] = saturate(pV->m_c[3]); |
| return res; |
| } |
| |
| static inline vec4F vec4F_from_color(const color_quad_i * pC) |
| { |
| vec4F res; |
| vec4F_set(&res, (float)pC->m_c[0], (float)pC->m_c[1], (float)pC->m_c[2], (float)pC->m_c[3]); |
| return res; |
| } |
| |
| static inline vec4F vec4F_add(const vec4F * pLHS, const vec4F * pRHS) |
| { |
| vec4F res; |
| vec4F_set(&res, pLHS->m_c[0] + pRHS->m_c[0], pLHS->m_c[1] + pRHS->m_c[1], pLHS->m_c[2] + pRHS->m_c[2], pLHS->m_c[3] + pRHS->m_c[3]); |
| return res; |
| } |
| |
| static inline vec4F vec4F_sub(const vec4F * pLHS, const vec4F * pRHS) |
| { |
| vec4F res; |
| vec4F_set(&res, pLHS->m_c[0] - pRHS->m_c[0], pLHS->m_c[1] - pRHS->m_c[1], pLHS->m_c[2] - pRHS->m_c[2], pLHS->m_c[3] - pRHS->m_c[3]); |
| return res; |
| } |
| |
| static inline float vec4F_dot(const vec4F * pLHS, const vec4F * pRHS) |
| { |
| return pLHS->m_c[0] * pRHS->m_c[0] + pLHS->m_c[1] * pRHS->m_c[1] + pLHS->m_c[2] * pRHS->m_c[2] + pLHS->m_c[3] * pRHS->m_c[3]; |
| } |
| |
| static inline vec4F vec4F_mul(const vec4F * pLHS, float s) |
| { |
| vec4F res; |
| vec4F_set(&res, pLHS->m_c[0] * s, pLHS->m_c[1] * s, pLHS->m_c[2] * s, pLHS->m_c[3] * s); |
| return res; |
| } |
| |
| static inline vec4F *vec4F_normalize_in_place( vec4F * pV) |
| { |
| float s = pV->m_c[0] * pV->m_c[0] + pV->m_c[1] * pV->m_c[1] + pV->m_c[2] * pV->m_c[2] + pV->m_c[3] * pV->m_c[3]; |
| |
| if (s != 0.0f) |
| { |
| s = 1.0f / sqrt(s); |
| |
| pV->m_c[0] *= s; |
| pV->m_c[1] *= s; |
| pV->m_c[2] *= s; |
| pV->m_c[3] *= s; |
| } |
| |
| return pV; |
| } |
| |
| static const uint32_t g_bc7_weights2[4] = { 0, 21, 43, 64 }; |
| static const uint32_t g_bc7_weights3[8] = { 0, 9, 18, 27, 37, 46, 55, 64 }; |
| static const uint32_t g_bc7_weights4[16] = { 0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64 }; |
| |
| // Precomputed weight constants used during least fit determination. For each entry in g_bc7_weights[]: w * w, (1.0f - w) * w, (1.0f - w) * (1.0f - w), w |
| static const float g_bc7_weights2x[4 * 4] = { 0.000000f, 0.000000f, 1.000000f, 0.000000f, 0.107666f, 0.220459f, 0.451416f, 0.328125f, 0.451416f, 0.220459f, 0.107666f, 0.671875f, 1.000000f, 0.000000f, 0.000000f, 1.000000f }; |
| static const float g_bc7_weights3x[8 * 4] = { 0.000000f, 0.000000f, 1.000000f, 0.000000f, 0.019775f, 0.120850f, 0.738525f, 0.140625f, 0.079102f, 0.202148f, 0.516602f, 0.281250f, 0.177979f, 0.243896f, 0.334229f, 0.421875f, 0.334229f, 0.243896f, 0.177979f, 0.578125f, 0.516602f, 0.202148f, |
| 0.079102f, 0.718750f, 0.738525f, 0.120850f, 0.019775f, 0.859375f, 1.000000f, 0.000000f, 0.000000f, 1.000000f }; |
| static const float g_bc7_weights4x[16 * 4] = { 0.000000f, 0.000000f, 1.000000f, 0.000000f, 0.003906f, 0.058594f, 0.878906f, 0.062500f, 0.019775f, 0.120850f, 0.738525f, 0.140625f, 0.041260f, 0.161865f, 0.635010f, 0.203125f, 0.070557f, 0.195068f, 0.539307f, 0.265625f, 0.107666f, 0.220459f, |
| 0.451416f, 0.328125f, 0.165039f, 0.241211f, 0.352539f, 0.406250f, 0.219727f, 0.249023f, 0.282227f, 0.468750f, 0.282227f, 0.249023f, 0.219727f, 0.531250f, 0.352539f, 0.241211f, 0.165039f, 0.593750f, 0.451416f, 0.220459f, 0.107666f, 0.671875f, 0.539307f, 0.195068f, 0.070557f, 0.734375f, |
| 0.635010f, 0.161865f, 0.041260f, 0.796875f, 0.738525f, 0.120850f, 0.019775f, 0.859375f, 0.878906f, 0.058594f, 0.003906f, 0.937500f, 1.000000f, 0.000000f, 0.000000f, 1.000000f }; |
| |
| static const int g_bc7_partition1[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }; |
| |
| static const int g_bc7_partition2[64 * 16] = |
| { |
| 0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1, 0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1, 0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1, 0,0,0,1,0,0,1,1,0,0,1,1,0,1,1,1, 0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,1, 0,0,1,1,0,1,1,1,0,1,1,1,1,1,1,1, 0,0,0,1,0,0,1,1,0,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,1,0,0,1,1,0,1,1,1, |
| 0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1, 0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,1,0,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1, 0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1, 0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1, |
| 0,0,0,0,1,0,0,0,1,1,1,0,1,1,1,1, 0,1,1,1,0,0,0,1,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,1,0,0,0,1,1,1,0, 0,1,1,1,0,0,1,1,0,0,0,1,0,0,0,0, 0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0, 0,0,0,0,1,0,0,0,1,1,0,0,1,1,1,0, 0,0,0,0,0,0,0,0,1,0,0,0,1,1,0,0, 0,1,1,1,0,0,1,1,0,0,1,1,0,0,0,1, |
| 0,0,1,1,0,0,0,1,0,0,0,1,0,0,0,0, 0,0,0,0,1,0,0,0,1,0,0,0,1,1,0,0, 0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0, 0,0,1,1,0,1,1,0,0,1,1,0,1,1,0,0, 0,0,0,1,0,1,1,1,1,1,1,0,1,0,0,0, 0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0, 0,1,1,1,0,0,0,1,1,0,0,0,1,1,1,0, 0,0,1,1,1,0,0,1,1,0,0,1,1,1,0,0, |
| 0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1, 0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1, 0,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0, 0,0,1,1,0,0,1,1,1,1,0,0,1,1,0,0, 0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0, 0,1,0,1,0,1,0,1,1,0,1,0,1,0,1,0, 0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1, 0,1,0,1,1,0,1,0,1,0,1,0,0,1,0,1, |
| 0,1,1,1,0,0,1,1,1,1,0,0,1,1,1,0, 0,0,0,1,0,0,1,1,1,1,0,0,1,0,0,0, 0,0,1,1,0,0,1,0,0,1,0,0,1,1,0,0, 0,0,1,1,1,0,1,1,1,1,0,1,1,1,0,0, 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, 0,0,1,1,1,1,0,0,1,1,0,0,0,0,1,1, 0,1,1,0,0,1,1,0,1,0,0,1,1,0,0,1, 0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0, |
| 0,1,0,0,1,1,1,0,0,1,0,0,0,0,0,0, 0,0,1,0,0,1,1,1,0,0,1,0,0,0,0,0, 0,0,0,0,0,0,1,0,0,1,1,1,0,0,1,0, 0,0,0,0,0,1,0,0,1,1,1,0,0,1,0,0, 0,1,1,0,1,1,0,0,1,0,0,1,0,0,1,1, 0,0,1,1,0,1,1,0,1,1,0,0,1,0,0,1, 0,1,1,0,0,0,1,1,1,0,0,1,1,1,0,0, 0,0,1,1,1,0,0,1,1,1,0,0,0,1,1,0, |
| 0,1,1,0,1,1,0,0,1,1,0,0,1,0,0,1, 0,1,1,0,0,0,1,1,0,0,1,1,1,0,0,1, 0,1,1,1,1,1,1,0,1,0,0,0,0,0,0,1, 0,0,0,1,1,0,0,0,1,1,1,0,0,1,1,1, 0,0,0,0,1,1,1,1,0,0,1,1,0,0,1,1, 0,0,1,1,0,0,1,1,1,1,1,1,0,0,0,0, 0,0,1,0,0,0,1,0,1,1,1,0,1,1,1,0, 0,1,0,0,0,1,0,0,0,1,1,1,0,1,1,1 |
| }; |
| |
| static const int g_bc7_table_anchor_index_second_subset[64] = |
| { |
| 15,15,15,15,15,15,15,15, 15,15,15,15,15,15,15,15, 15, 2, 8, 2, 2, 8, 8,15, 2, 8, 2, 2, 8, 8, 2, 2, 15,15, 6, 8, 2, 8,15,15, 2, 8, 2, 2, 2,15,15, 6, 6, 2, 6, 8,15,15, 2, 2, 15,15,15,15,15, 2, 2,15 |
| }; |
| |
| static const int g_bc7_partition3[64 * 16] = |
| { |
| 0,0,1,1,0,0,1,1,0,2,2,1,2,2,2,2, 0,0,0,1,0,0,1,1,2,2,1,1,2,2,2,1, 0,0,0,0,2,0,0,1,2,2,1,1,2,2,1,1, 0,2,2,2,0,0,2,2,0,0,1,1,0,1,1,1, 0,0,0,0,0,0,0,0,1,1,2,2,1,1,2,2, 0,0,1,1,0,0,1,1,0,0,2,2,0,0,2,2, 0,0,2,2,0,0,2,2,1,1,1,1,1,1,1,1, 0,0,1,1,0,0,1,1,2,2,1,1,2,2,1,1, |
| 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2, 0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2, 0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2, 0,0,1,2,0,0,1,2,0,0,1,2,0,0,1,2, 0,1,1,2,0,1,1,2,0,1,1,2,0,1,1,2, 0,1,2,2,0,1,2,2,0,1,2,2,0,1,2,2, 0,0,1,1,0,1,1,2,1,1,2,2,1,2,2,2, 0,0,1,1,2,0,0,1,2,2,0,0,2,2,2,0, |
| 0,0,0,1,0,0,1,1,0,1,1,2,1,1,2,2, 0,1,1,1,0,0,1,1,2,0,0,1,2,2,0,0, 0,0,0,0,1,1,2,2,1,1,2,2,1,1,2,2, 0,0,2,2,0,0,2,2,0,0,2,2,1,1,1,1, 0,1,1,1,0,1,1,1,0,2,2,2,0,2,2,2, 0,0,0,1,0,0,0,1,2,2,2,1,2,2,2,1, 0,0,0,0,0,0,1,1,0,1,2,2,0,1,2,2, 0,0,0,0,1,1,0,0,2,2,1,0,2,2,1,0, |
| 0,1,2,2,0,1,2,2,0,0,1,1,0,0,0,0, 0,0,1,2,0,0,1,2,1,1,2,2,2,2,2,2, 0,1,1,0,1,2,2,1,1,2,2,1,0,1,1,0, 0,0,0,0,0,1,1,0,1,2,2,1,1,2,2,1, 0,0,2,2,1,1,0,2,1,1,0,2,0,0,2,2, 0,1,1,0,0,1,1,0,2,0,0,2,2,2,2,2, 0,0,1,1,0,1,2,2,0,1,2,2,0,0,1,1, 0,0,0,0,2,0,0,0,2,2,1,1,2,2,2,1, |
| 0,0,0,0,0,0,0,2,1,1,2,2,1,2,2,2, 0,2,2,2,0,0,2,2,0,0,1,2,0,0,1,1, 0,0,1,1,0,0,1,2,0,0,2,2,0,2,2,2, 0,1,2,0,0,1,2,0,0,1,2,0,0,1,2,0, 0,0,0,0,1,1,1,1,2,2,2,2,0,0,0,0, 0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,0, 0,1,2,0,2,0,1,2,1,2,0,1,0,1,2,0, 0,0,1,1,2,2,0,0,1,1,2,2,0,0,1,1, |
| 0,0,1,1,1,1,2,2,2,2,0,0,0,0,1,1, 0,1,0,1,0,1,0,1,2,2,2,2,2,2,2,2, 0,0,0,0,0,0,0,0,2,1,2,1,2,1,2,1, 0,0,2,2,1,1,2,2,0,0,2,2,1,1,2,2, 0,0,2,2,0,0,1,1,0,0,2,2,0,0,1,1, 0,2,2,0,1,2,2,1,0,2,2,0,1,2,2,1, 0,1,0,1,2,2,2,2,2,2,2,2,0,1,0,1, 0,0,0,0,2,1,2,1,2,1,2,1,2,1,2,1, |
| 0,1,0,1,0,1,0,1,0,1,0,1,2,2,2,2, 0,2,2,2,0,1,1,1,0,2,2,2,0,1,1,1, 0,0,0,2,1,1,1,2,0,0,0,2,1,1,1,2, 0,0,0,0,2,1,1,2,2,1,1,2,2,1,1,2, 0,2,2,2,0,1,1,1,0,1,1,1,0,2,2,2, 0,0,0,2,1,1,1,2,1,1,1,2,0,0,0,2, 0,1,1,0,0,1,1,0,0,1,1,0,2,2,2,2, 0,0,0,0,0,0,0,0,2,1,1,2,2,1,1,2, |
| 0,1,1,0,0,1,1,0,2,2,2,2,2,2,2,2, 0,0,2,2,0,0,1,1,0,0,1,1,0,0,2,2, 0,0,2,2,1,1,2,2,1,1,2,2,0,0,2,2, 0,0,0,0,0,0,0,0,0,0,0,0,2,1,1,2, 0,0,0,2,0,0,0,1,0,0,0,2,0,0,0,1, 0,2,2,2,1,2,2,2,0,2,2,2,1,2,2,2, 0,1,0,1,2,2,2,2,2,2,2,2,2,2,2,2, 0,1,1,1,2,0,1,1,2,2,0,1,2,2,2,0, |
| }; |
| |
| static const int g_bc7_table_anchor_index_third_subset_1[64] = |
| { |
| 3, 3,15,15, 8, 3,15,15, 8, 8, 6, 6, 6, 5, 3, 3, 3, 3, 8,15, 3, 3, 6,10, 5, 8, 8, 6, 8, 5,15,15, 8,15, 3, 5, 6,10, 8,15, 15, 3,15, 5,15,15,15,15, 3,15, 5, 5, 5, 8, 5,10, 5,10, 8,13,15,12, 3, 3 |
| }; |
| |
| static const int g_bc7_table_anchor_index_third_subset_2[64] = |
| { |
| 15, 8, 8, 3,15,15, 3, 8, 15,15,15,15,15,15,15, 8, 15, 8,15, 3,15, 8,15, 8, 3,15, 6,10,15,15,10, 8, 15, 3,15,10,10, 8, 9,10, 6,15, 8,15, 3, 6, 6, 8, 15, 3,15,15,15,15,15,15, 15,15,15,15, 3,15,15, 8 |
| }; |
| |
| static const int g_bc7_num_subsets[8] = { 3, 2, 3, 2, 1, 1, 1, 2 }; |
| static const int g_bc7_partition_bits[8] = { 4, 6, 6, 6, 0, 0, 0, 6 }; |
| [[maybe_unused]] static const int g_bc7_rotation_bits[8] = { 0, 0, 0, 0, 2, 2, 0, 0 }; |
| static const int g_bc7_color_index_bitcount[8] = { 3, 3, 2, 2, 2, 2, 4, 2 }; |
| static int get_bc7_color_index_size( int mode, int index_selection_bit) { return g_bc7_color_index_bitcount[mode] + index_selection_bit; } |
| static int g_bc7_alpha_index_bitcount[8] = { 0, 0, 0, 0, 3, 2, 4, 2 }; |
| static int get_bc7_alpha_index_size( int mode, int index_selection_bit) { return g_bc7_alpha_index_bitcount[mode] - index_selection_bit; } |
| static const int g_bc7_mode_has_p_bits[8] = { 1, 1, 0, 1, 0, 0, 1, 1 }; |
| static const int g_bc7_mode_has_shared_p_bits[8] = { 0, 1, 0, 0, 0, 0, 0, 0 }; |
| static const int g_bc7_color_precision_table[8] = { 4, 6, 5, 7, 5, 7, 7, 5 }; |
| [[maybe_unused]] static const int g_bc7_color_precision_plus_pbit_table[8] = { 5, 7, 5, 8, 5, 7, 8, 6 }; |
| static const int g_bc7_alpha_precision_table[8] = { 0, 0, 0, 0, 6, 8, 7, 5 }; |
| [[maybe_unused]] static const int g_bc7_alpha_precision_plus_pbit_table[8] = { 0, 0, 0, 0, 6, 8, 8, 6 }; |
| static bool get_bc7_mode_has_seperate_alpha_selectors( int mode) { return (mode == 4) || (mode == 5); } |
| |
| struct endpoint_err |
| { |
| uint16_t m_error; |
| uint8_t m_lo; |
| uint8_t m_hi; |
| }; |
| |
| // Guarded store into endpoint_err::m_error (a real uint16_t). The stored quantity |
| // is always a single-channel squared error (k-c)^2 with k,c in [0,255], so it is |
| // <= 255^2 = 65025 <= UINT16_MAX by construction. This assert verifies that |
| // invariant on every store; if it ever fires (e.g. a weighted error gets routed |
| // here), the value would silently truncate at 16 bits, so the assert must hold. |
| static inline uint16_t safe_cast_uint16(int v) |
| { |
| assert(v >= 0 && v <= (int)UINT16_MAX); |
| return (uint16_t)v; |
| } |
| |
| static endpoint_err g_bc7_mode_1_optimal_endpoints[256][2]; // [c][pbit] |
| const uint32_t BC7E_MODE_1_OPTIMAL_INDEX = 2; |
| |
| static endpoint_err g_bc7_mode_7_optimal_endpoints[256][2][2]; // [c][pbit][hp][lp] |
| const uint32_t BC7E_MODE_7_OPTIMAL_INDEX = 1; |
| |
| static endpoint_err g_bc7_mode_6_optimal_endpoints[256][2][2]; // [c][hp][lp] |
| const uint32_t BC7E_MODE_6_OPTIMAL_INDEX = 5; |
| |
| static uint32_t g_bc7_mode_4_optimal_endpoints3[256]; // [c] |
| static uint32_t g_bc7_mode_4_optimal_endpoints2[256]; // [c] |
| const uint32_t BC7E_MODE_4_OPTIMAL_INDEX3 = 2; |
| const uint32_t BC7E_MODE_4_OPTIMAL_INDEX2 = 1; |
| |
| static uint32_t g_bc7_mode_5_optimal_endpoints[256]; // [c] |
| const uint32_t BC7E_MODE_5_OPTIMAL_INDEX = 1; |
| |
| static endpoint_err g_bc7_mode_0_optimal_endpoints[256][2][2]; // [c][hp][lp] |
| const uint32_t BC7E_MODE_0_OPTIMAL_INDEX = 2; |
| |
| static bool g_codec_initialized; |
| |
| void bc7e_compress_block_init() |
| { |
| if (g_codec_initialized) |
| return; |
| |
| // Mode 0: 444.1 |
| for ( int c = 0; c < 256; c++) |
| { |
| for ( uint32_t hp = 0; hp < 2; hp++) |
| { |
| for ( uint32_t lp = 0; lp < 2; lp++) |
| { |
| endpoint_err best; |
| best.m_error = safe_cast_uint16(UINT16_MAX); |
| |
| for ( uint32_t l = 0; l < 16; l++) |
| { |
| uint32_t low = ((l << 1) | lp) << 3; |
| low |= (low >> 5); |
| |
| for ( uint32_t h = 0; h < 16; h++) |
| { |
| uint32_t high = ((h << 1) | hp) << 3; |
| high |= (high >> 5); |
| |
| const int k = (low * (64 - g_bc7_weights3[BC7E_MODE_0_OPTIMAL_INDEX]) + high * g_bc7_weights3[BC7E_MODE_0_OPTIMAL_INDEX] + 32) >> 6; |
| |
| const int err = (k - c) * (k - c); |
| if (err < best.m_error) |
| { |
| best.m_error = safe_cast_uint16(err); |
| best.m_lo = (uint8_t)l; |
| best.m_hi = (uint8_t)h; |
| } |
| } // h |
| } // l |
| |
| g_bc7_mode_0_optimal_endpoints[c][hp][lp] = best; |
| } // lp |
| } // hp |
| } // c |
| |
| // Mode 1: 666.1 |
| for ( int c = 0; c < 256; c++) |
| { |
| for ( uint32_t lp = 0; lp < 2; lp++) |
| { |
| endpoint_err best; |
| best.m_error = safe_cast_uint16(UINT16_MAX); |
| |
| for ( uint32_t l = 0; l < 64; l++) |
| { |
| uint32_t low = ((l << 1) | lp) << 1; |
| low |= (low >> 7); |
| |
| for ( uint32_t h = 0; h < 64; h++) |
| { |
| uint32_t high = ((h << 1) | lp) << 1; |
| high |= (high >> 7); |
| |
| const int k = (low * (64 - g_bc7_weights3[BC7E_MODE_1_OPTIMAL_INDEX]) + high * g_bc7_weights3[BC7E_MODE_1_OPTIMAL_INDEX] + 32) >> 6; |
| |
| const int err = (k - c) * (k - c); |
| if (err < best.m_error) |
| { |
| best.m_error = safe_cast_uint16(err); |
| best.m_lo = (uint8_t)l; |
| best.m_hi = (uint8_t)h; |
| } |
| } // h |
| } // l |
| |
| g_bc7_mode_1_optimal_endpoints[c][lp] = best; |
| } // lp |
| } // c |
| |
| // Mode 6: 777.1 4-bit indices |
| for ( int c = 0; c < 256; c++) |
| { |
| for ( uint32_t hp = 0; hp < 2; hp++) |
| { |
| for ( uint32_t lp = 0; lp < 2; lp++) |
| { |
| endpoint_err best; |
| best.m_error = safe_cast_uint16(UINT16_MAX); |
| |
| for ( uint32_t l = 0; l < 128; l++) |
| { |
| uint32_t low = (l << 1) | lp; |
| |
| for ( uint32_t h = 0; h < 128; h++) |
| { |
| uint32_t high = (h << 1) | hp; |
| |
| const int k = (low * (64 - g_bc7_weights4[BC7E_MODE_6_OPTIMAL_INDEX]) + high * g_bc7_weights4[BC7E_MODE_6_OPTIMAL_INDEX] + 32) >> 6; |
| |
| const int err = (k - c) * (k - c); |
| if (err < best.m_error) |
| { |
| best.m_error = safe_cast_uint16(err); |
| best.m_lo = (uint8_t)l; |
| best.m_hi = (uint8_t)h; |
| } |
| } // h |
| } // l |
| |
| g_bc7_mode_6_optimal_endpoints[c][hp][lp] = best; |
| } // lp |
| } // hp |
| } // c |
| |
| // Mode 5: 777 2-bit indices |
| for ( int c = 0; c < 256; c++) |
| { |
| endpoint_err best; |
| best.m_error = safe_cast_uint16(UINT16_MAX); |
| best.m_lo = 0; |
| best.m_hi = 0; |
| |
| for ( uint32_t l = 0; l < 128; l++) |
| { |
| uint32_t low = l << 1; |
| low |= (low >> 7); |
| |
| for ( uint32_t h = 0; h < 128; h++) |
| { |
| uint32_t high = h << 1; |
| high |= (high >> 7); |
| |
| const int k = (low * (64 - g_bc7_weights2[BC7E_MODE_5_OPTIMAL_INDEX]) + high * g_bc7_weights2[BC7E_MODE_5_OPTIMAL_INDEX] + 32) >> 6; |
| |
| const int err = (k - c) * (k - c); |
| if (err < best.m_error) |
| { |
| best.m_error = safe_cast_uint16(err); |
| best.m_lo = (uint8_t)l; |
| best.m_hi = (uint8_t)h; |
| } |
| } // h |
| } // l |
| |
| g_bc7_mode_5_optimal_endpoints[c] = (uint32_t)best.m_lo | (((uint32_t)best.m_hi) << 8); |
| |
| } // c |
| |
| |
| //Mode 4: 555 3-bit indices |
| for ( int c = 0; c < 256; c++) |
| { |
| endpoint_err best; |
| best.m_error = safe_cast_uint16(UINT16_MAX); |
| best.m_lo = 0; |
| best.m_hi = 0; |
| |
| for ( uint32_t l = 0; l < 32; l++) |
| { |
| uint32_t low = l << 3; |
| low |= (low >> 5); |
| |
| for ( uint32_t h = 0; h < 32; h++) |
| { |
| uint32_t high = h << 3; |
| high |= (high >> 5); |
| |
| const int k = (low * (64 - g_bc7_weights3[BC7E_MODE_4_OPTIMAL_INDEX3]) + high * g_bc7_weights3[BC7E_MODE_4_OPTIMAL_INDEX3] + 32) >> 6; |
| |
| const int err = (k - c) * (k - c); |
| if (err < best.m_error) |
| { |
| best.m_error = safe_cast_uint16(err); |
| best.m_lo = (uint8_t)l; |
| best.m_hi = (uint8_t)h; |
| } |
| } // h |
| } // l |
| |
| g_bc7_mode_4_optimal_endpoints3[c] = (uint32_t)best.m_lo | (((uint32_t)best.m_hi) << 8); |
| |
| } // c |
| |
| // Mode 4: 555 2-bit indices |
| for ( int c = 0; c < 256; c++) |
| { |
| endpoint_err best; |
| best.m_error = safe_cast_uint16(UINT16_MAX); |
| best.m_lo = 0; |
| best.m_hi = 0; |
| |
| for ( uint32_t l = 0; l < 32; l++) |
| { |
| uint32_t low = l << 3; |
| low |= (low >> 5); |
| |
| for ( uint32_t h = 0; h < 32; h++) |
| { |
| uint32_t high = h << 3; |
| high |= (high >> 5); |
| |
| const int k = (low * (64 - g_bc7_weights2[BC7E_MODE_4_OPTIMAL_INDEX2]) + high * g_bc7_weights2[BC7E_MODE_4_OPTIMAL_INDEX2] + 32) >> 6; |
| |
| const int err = (k - c) * (k - c); |
| if (err < best.m_error) |
| { |
| best.m_error = safe_cast_uint16(err); |
| best.m_lo = (uint8_t)l; |
| best.m_hi = (uint8_t)h; |
| } |
| } // h |
| } // l |
| |
| g_bc7_mode_4_optimal_endpoints2[c] = (uint32_t)best.m_lo | (((uint32_t)best.m_hi) << 8); |
| |
| } // c |
| |
| // Mode 7: 555.1 2-bit indices |
| for ( int c = 0; c < 256; c++) |
| { |
| endpoint_err best; |
| best.m_error = safe_cast_uint16(UINT16_MAX); |
| best.m_lo = 0; |
| best.m_hi = 0; |
| |
| for ( uint32_t hp = 0; hp < 2; hp++) |
| { |
| for ( uint32_t lp = 0; lp < 2; lp++) |
| { |
| for ( uint32_t l = 0; l < 32; l++) |
| { |
| uint32_t low = ((l << 1) | lp) << 2; |
| low |= (low >> 6); |
| |
| for ( uint32_t h = 0; h < 32; h++) |
| { |
| uint32_t high = ((h << 1) | hp) << 2; |
| high |= (high >> 6); |
| |
| const int k = (low * (64 - g_bc7_weights2[BC7E_MODE_7_OPTIMAL_INDEX]) + high * g_bc7_weights2[BC7E_MODE_7_OPTIMAL_INDEX] + 32) >> 6; |
| |
| const int err = (k - c) * (k - c); |
| if (err < best.m_error) |
| { |
| best.m_error = safe_cast_uint16(err); |
| best.m_lo = (uint8_t)l; |
| best.m_hi = (uint8_t)h; |
| } |
| } // h |
| } // l |
| |
| g_bc7_mode_7_optimal_endpoints[c][hp][lp] = best; |
| |
| } // hp |
| |
| } // lp |
| |
| } // c |
| |
| g_codec_initialized = true; |
| } |
| |
| static void compute_least_squares_endpoints_rgba(uint32_t N, const int * pSelectors, const vec4F * pSelector_weights, vec4F * pXl, vec4F * pXh, const color_quad_i * pColors) |
| { |
| // Least squares using normal equations: http://www.cs.cornell.edu/~bindel/class/cs3220-s12/notes/lec10.pdf |
| // I did this in matrix form first, expanded out all the ops, then optimized it a bit. |
| float z00 = 0.0f, z01 = 0.0f, z10 = 0.0f, z11 = 0.0f; |
| float q00_r = 0.0f, q10_r = 0.0f, t_r = 0.0f; |
| float q00_g = 0.0f, q10_g = 0.0f, t_g = 0.0f; |
| float q00_b = 0.0f, q10_b = 0.0f, t_b = 0.0f; |
| float q00_a = 0.0f, q10_a = 0.0f, t_a = 0.0f; |
| for ( uint32_t i = 0; i < N; i++) |
| { |
| const uint32_t sel = pSelectors[i]; |
| |
| z00 += pSelector_weights[sel].m_c[0]; |
| z10 += pSelector_weights[sel].m_c[1]; |
| z11 += pSelector_weights[sel].m_c[2]; |
| |
| float w = pSelector_weights[sel].m_c[3]; |
| |
| q00_r += w * (int)pColors[i].m_c[0]; t_r += (int)pColors[i].m_c[0]; |
| q00_g += w * (int)pColors[i].m_c[1]; t_g += (int)pColors[i].m_c[1]; |
| q00_b += w * (int)pColors[i].m_c[2]; t_b += (int)pColors[i].m_c[2]; |
| q00_a += w * (int)pColors[i].m_c[3]; t_a += (int)pColors[i].m_c[3]; |
| } |
| |
| q10_r = t_r - q00_r; |
| q10_g = t_g - q00_g; |
| q10_b = t_b - q00_b; |
| q10_a = t_a - q00_a; |
| |
| z01 = z10; |
| |
| float det = z00 * z11 - z01 * z10; |
| if (det != 0.0f) |
| det = 1.0f / det; |
| |
| float iz00, iz01, iz10, iz11; |
| iz00 = z11 * det; |
| iz01 = -z01 * det; |
| iz10 = -z10 * det; |
| iz11 = z00 * det; |
| |
| pXl->m_c[0] = (float)(iz00 * q00_r + iz01 * q10_r); pXh->m_c[0] = (float)(iz10 * q00_r + iz11 * q10_r); |
| pXl->m_c[1] = (float)(iz00 * q00_g + iz01 * q10_g); pXh->m_c[1] = (float)(iz10 * q00_g + iz11 * q10_g); |
| pXl->m_c[2] = (float)(iz00 * q00_b + iz01 * q10_b); pXh->m_c[2] = (float)(iz10 * q00_b + iz11 * q10_b); |
| pXl->m_c[3] = (float)(iz00 * q00_a + iz01 * q10_a); pXh->m_c[3] = (float)(iz10 * q00_a + iz11 * q10_a); |
| } |
| |
| static void compute_least_squares_endpoints_rgb(uint32_t N, const int * pSelectors, const vec4F * pSelector_weights, vec4F * pXl, vec4F * pXh, const color_quad_i * pColors) |
| { |
| // Least squares using normal equations: http://www.cs.cornell.edu/~bindel/class/cs3220-s12/notes/lec10.pdf |
| // I did this in matrix form first, expanded out all the ops, then optimized it a bit. |
| float z00 = 0.0f, z01 = 0.0f, z10 = 0.0f, z11 = 0.0f; |
| float q00_r = 0.0f, q10_r = 0.0f, t_r = 0.0f; |
| float q00_g = 0.0f, q10_g = 0.0f, t_g = 0.0f; |
| float q00_b = 0.0f, q10_b = 0.0f, t_b = 0.0f; |
| for ( uint32_t i = 0; i < N; i++) |
| { |
| const uint32_t sel = pSelectors[i]; |
| |
| z00 += pSelector_weights[sel].m_c[0]; |
| z10 += pSelector_weights[sel].m_c[1]; |
| z11 += pSelector_weights[sel].m_c[2]; |
| float w = pSelector_weights[sel].m_c[3]; |
| |
| q00_r += w * (int)pColors[i].m_c[0]; t_r += (int)pColors[i].m_c[0]; |
| q00_g += w * (int)pColors[i].m_c[1]; t_g += (int)pColors[i].m_c[1]; |
| q00_b += w * (int)pColors[i].m_c[2]; t_b += (int)pColors[i].m_c[2]; |
| } |
| |
| q10_r = t_r - q00_r; |
| q10_g = t_g - q00_g; |
| q10_b = t_b - q00_b; |
| |
| z01 = z10; |
| |
| float det = z00 * z11 - z01 * z10; |
| if (det != 0.0f) |
| det = 1.0f / det; |
| |
| float iz00, iz01, iz10, iz11; |
| iz00 = z11 * det; |
| iz01 = -z01 * det; |
| iz10 = -z10 * det; |
| iz11 = z00 * det; |
| |
| pXl->m_c[0] = (float)(iz00 * q00_r + iz01 * q10_r); pXh->m_c[0] = (float)(iz10 * q00_r + iz11 * q10_r); |
| pXl->m_c[1] = (float)(iz00 * q00_g + iz01 * q10_g); pXh->m_c[1] = (float)(iz10 * q00_g + iz11 * q10_g); |
| pXl->m_c[2] = (float)(iz00 * q00_b + iz01 * q10_b); pXh->m_c[2] = (float)(iz10 * q00_b + iz11 * q10_b); |
| } |
| |
| static void compute_least_squares_endpoints_a(uint32_t N, const int * pSelectors, const vec4F * pSelector_weights, float * pXl, float * pXh, const color_quad_i * pColors) |
| { |
| // Least squares using normal equations: http://www.cs.cornell.edu/~bindel/class/cs3220-s12/notes/lec10.pdf |
| // I did this in matrix form first, expanded out all the ops, then optimized it a bit. |
| float z00 = 0.0f, z01 = 0.0f, z10 = 0.0f, z11 = 0.0f; |
| float q00_a = 0.0f, q10_a = 0.0f, t_a = 0.0f; |
| for ( uint32_t i = 0; i < N; i++) |
| { |
| const uint32_t sel = pSelectors[i]; |
| |
| z00 += pSelector_weights[sel].m_c[0]; |
| z10 += pSelector_weights[sel].m_c[1]; |
| z11 += pSelector_weights[sel].m_c[2]; |
| float w = pSelector_weights[sel].m_c[3]; |
| |
| q00_a += w * (int)pColors[i].m_c[3]; t_a += (int)pColors[i].m_c[3]; |
| } |
| |
| q10_a = t_a - q00_a; |
| |
| z01 = z10; |
| |
| float det = z00 * z11 - z01 * z10; |
| if (det != 0.0f) |
| det = 1.0f / det; |
| |
| float iz00, iz01, iz10, iz11; |
| iz00 = z11 * det; |
| iz01 = -z01 * det; |
| iz10 = -z10 * det; |
| iz11 = z00 * det; |
| |
| *pXl = (float)(iz00 * q00_a + iz01 * q10_a); *pXh = (float)(iz10 * q00_a + iz11 * q10_a); |
| } |
| |
| struct color_cell_compressor_params |
| { |
| uint32_t m_num_selector_weights; |
| const uint32_t * m_pSelector_weights; |
| const vec4F * m_pSelector_weightsx; |
| uint32_t m_comp_bits; |
| uint32_t m_weights[4]; |
| bool m_has_alpha; |
| bool m_has_pbits; |
| bool m_endpoints_share_pbit; |
| bool m_perceptual; |
| }; |
| |
| static inline void color_cell_compressor_params_clear( color_cell_compressor_params * p) |
| { |
| p->m_num_selector_weights = 0; |
| p->m_pSelector_weights = NULL; |
| p->m_pSelector_weightsx = NULL; |
| p->m_comp_bits = 0; |
| p->m_perceptual = false; |
| p->m_weights[0] = 1; |
| p->m_weights[1] = 1; |
| p->m_weights[2] = 1; |
| p->m_weights[3] = 1; |
| p->m_has_alpha = false; |
| p->m_has_pbits = false; |
| p->m_endpoints_share_pbit = false; |
| } |
| |
| struct color_cell_compressor_results |
| { |
| uint64_t m_best_overall_err; |
| color_quad_i m_low_endpoint; |
| color_quad_i m_high_endpoint; |
| uint32_t m_pbits[2]; |
| int *m_pSelectors; |
| int *m_pSelectors_temp; |
| // True if this subset's result was produced by the precomputed "one color" |
| // optimal-endpoint lookup tables (the solid/allSame path or the average-color |
| // candidate). Those place endpoints at extreme positions that only land at one |
| // fixed weight -- a hint to callers that this subset is "weird" (fragile under |
| // lossy weight recoding). |
| bool m_used_lut; |
| }; |
| |
| static inline color_quad_i scale_color(const color_quad_i * pC, const color_cell_compressor_params * pParams) |
| { |
| color_quad_i results; |
| |
| const uint32_t n = pParams->m_comp_bits + (pParams->m_has_pbits ? 1 : 0); |
| assert((n >= 4) && (n <= 8)); |
| |
| for ( uint32_t i = 0; i < 4; i++) |
| { |
| uint32_t v = pC->m_c[i] << (8 - n); |
| v |= (v >> n); |
| assert(v <= 255); |
| results.m_c[i] = v; |
| } |
| |
| return results; |
| } |
| |
| static const float pr_weight = (.5f / (1.0f - .2126f)) * (.5f / (1.0f - .2126f)); |
| static const float pb_weight = (.5f / (1.0f - .0722f)) * (.5f / (1.0f - .0722f)); |
| |
| static inline uint64_t compute_color_distance_rgb(const color_quad_i * pE1, const color_quad_i * pE2, bool perceptual, const uint32_t weights[4]) |
| { |
| if (perceptual) |
| { |
| const float l1 = pE1->m_c[0] * .2126f + pE1->m_c[1] * .7152f + pE1->m_c[2] * .0722f; |
| const float cr1 = pE1->m_c[0] - l1; |
| const float cb1 = pE1->m_c[2] - l1; |
| |
| const float l2 = pE2->m_c[0] * .2126f + pE2->m_c[1] * .7152f + pE2->m_c[2] * .0722f; |
| const float cr2 = pE2->m_c[0] - l2; |
| const float cb2 = pE2->m_c[2] - l2; |
| |
| float dl = l1 - l2; |
| float dcr = cr1 - cr2; |
| float dcb = cb1 - cb2; |
| |
| return (int64_t)(weights[0] * (dl * dl) + weights[1] * pr_weight * (dcr * dcr) + weights[2] * pb_weight * (dcb * dcb)); |
| } |
| else |
| { |
| float dr = (float)pE1->m_c[0] - (float)pE2->m_c[0]; |
| float dg = (float)pE1->m_c[1] - (float)pE2->m_c[1]; |
| float db = (float)pE1->m_c[2] - (float)pE2->m_c[2]; |
| |
| return (int64_t)(weights[0] * dr * dr + weights[1] * dg * dg + weights[2] * db * db); |
| } |
| } |
| |
| static inline uint64_t compute_color_distance_rgba(const color_quad_i * pE1, const color_quad_i * pE2, bool perceptual, const uint32_t weights[4]) |
| { |
| float da = (float)pE1->m_c[3] - (float)pE2->m_c[3]; |
| float a_err = weights[3] * (da * da); |
| |
| if (perceptual) |
| { |
| const float l1 = pE1->m_c[0] * .2126f + pE1->m_c[1] * .7152f + pE1->m_c[2] * .0722f; |
| const float cr1 = pE1->m_c[0] - l1; |
| const float cb1 = pE1->m_c[2] - l1; |
| |
| const float l2 = pE2->m_c[0] * .2126f + pE2->m_c[1] * .7152f + pE2->m_c[2] * .0722f; |
| const float cr2 = pE2->m_c[0] - l2; |
| const float cb2 = pE2->m_c[2] - l2; |
| |
| float dl = l1 - l2; |
| float dcr = cr1 - cr2; |
| float dcb = cb1 - cb2; |
| |
| return (int64_t)(weights[0] * (dl * dl) + weights[1] * pr_weight * (dcr * dcr) + weights[2] * pb_weight * (dcb * dcb) + a_err); |
| } |
| else |
| { |
| float dr = (float)pE1->m_c[0] - (float)pE2->m_c[0]; |
| float dg = (float)pE1->m_c[1] - (float)pE2->m_c[1]; |
| float db = (float)pE1->m_c[2] - (float)pE2->m_c[2]; |
| |
| return (int64_t)(weights[0] * dr * dr + weights[1] * dg * dg + weights[2] * db * db + a_err); |
| } |
| } |
| |
| static uint64_t pack_mode1_to_one_color(const color_cell_compressor_params * pParams, color_cell_compressor_results * pResults, uint32_t r, uint32_t g, uint32_t b, |
| int * pSelectors, uint32_t num_pixels, const color_quad_i * pPixels) |
| { |
| uint32_t best_err = UINT_MAX; |
| uint32_t best_p = 0; |
| |
| for ( uint32_t p = 0; p < 2; p++) |
| { |
| uint32_t err = g_bc7_mode_1_optimal_endpoints[r][p].m_error + g_bc7_mode_1_optimal_endpoints[g][p].m_error + g_bc7_mode_1_optimal_endpoints[b][p].m_error; |
| if (err < best_err) |
| { |
| best_err = err; |
| best_p = p; |
| } |
| } |
| |
| const endpoint_err *pEr = &g_bc7_mode_1_optimal_endpoints[r][best_p]; |
| const endpoint_err *pEg = &g_bc7_mode_1_optimal_endpoints[g][best_p]; |
| const endpoint_err *pEb = &g_bc7_mode_1_optimal_endpoints[b][best_p]; |
| |
| color_quad_i_set(&pResults->m_low_endpoint, pEr->m_lo, pEg->m_lo, pEb->m_lo, 0); |
| color_quad_i_set(&pResults->m_high_endpoint, pEr->m_hi, pEg->m_hi, pEb->m_hi, 0); |
| pResults->m_pbits[0] = best_p; |
| pResults->m_pbits[1] = 0; |
| |
| for ( uint32_t i = 0; i < num_pixels; i++) |
| pSelectors[i] = BC7E_MODE_1_OPTIMAL_INDEX; |
| |
| color_quad_i p; |
| |
| for ( uint32_t i = 0; i < 3; i++) |
| { |
| uint32_t low = ((pResults->m_low_endpoint.m_c[i] << 1) | pResults->m_pbits[0]) << 1; |
| low |= (low >> 7); |
| |
| uint32_t high = ((pResults->m_high_endpoint.m_c[i] << 1) | pResults->m_pbits[0]) << 1; |
| high |= (high >> 7); |
| |
| p.m_c[i] = (low * (64 - g_bc7_weights3[BC7E_MODE_1_OPTIMAL_INDEX]) + high * g_bc7_weights3[BC7E_MODE_1_OPTIMAL_INDEX] + 32) >> 6; |
| } |
| |
| p.m_c[3] = 255; |
| |
| uint64_t total_err = 0; |
| for ( uint32_t i = 0; i < num_pixels; i++) |
| total_err += compute_color_distance_rgb(&p, &pPixels[i], pParams->m_perceptual, pParams->m_weights); |
| |
| pResults->m_best_overall_err = total_err; |
| |
| return total_err; |
| } |
| |
| static uint64_t pack_mode24_to_one_color(const color_cell_compressor_params * pParams, color_cell_compressor_results * pResults, uint32_t r, uint32_t g, uint32_t b, |
| int * pSelectors, uint32_t num_pixels, const color_quad_i * pPixels) |
| { |
| uint32_t er, eg, eb; |
| |
| if (pParams->m_num_selector_weights == 8) |
| { |
| er = g_bc7_mode_4_optimal_endpoints3[r]; |
| eg = g_bc7_mode_4_optimal_endpoints3[g]; |
| eb = g_bc7_mode_4_optimal_endpoints3[b]; |
| } |
| else |
| { |
| er = g_bc7_mode_4_optimal_endpoints2[r]; |
| eg = g_bc7_mode_4_optimal_endpoints2[g]; |
| eb = g_bc7_mode_4_optimal_endpoints2[b]; |
| } |
| |
| color_quad_i_set(&pResults->m_low_endpoint, er & 0xFF, eg & 0xFF, eb & 0xFF, 0); |
| color_quad_i_set(&pResults->m_high_endpoint, er >> 8, eg >> 8, eb >> 8, 0); |
| |
| for ( uint32_t i = 0; i < num_pixels; i++) |
| pSelectors[i] = (pParams->m_num_selector_weights == 8) ? BC7E_MODE_4_OPTIMAL_INDEX3 : BC7E_MODE_4_OPTIMAL_INDEX2; |
| |
| color_quad_i p; |
| |
| for ( uint32_t i = 0; i < 3; i++) |
| { |
| uint32_t low = pResults->m_low_endpoint.m_c[i] << 3; |
| low |= (low >> 5); |
| |
| uint32_t high = pResults->m_high_endpoint.m_c[i] << 3; |
| high |= (high >> 5); |
| |
| if (pParams->m_num_selector_weights == 8) |
| p.m_c[i] = (low * (64 - g_bc7_weights3[BC7E_MODE_4_OPTIMAL_INDEX3]) + high * g_bc7_weights3[BC7E_MODE_4_OPTIMAL_INDEX3] + 32) >> 6; |
| else |
| p.m_c[i] = (low * (64 - g_bc7_weights2[BC7E_MODE_4_OPTIMAL_INDEX2]) + high * g_bc7_weights2[BC7E_MODE_4_OPTIMAL_INDEX2] + 32) >> 6; |
| } |
| |
| p.m_c[3] = 255; |
| |
| uint64_t total_err = 0; |
| for ( uint32_t i = 0; i < num_pixels; i++) |
| total_err += compute_color_distance_rgb(&p, &pPixels[i], pParams->m_perceptual, pParams->m_weights); |
| |
| pResults->m_best_overall_err = total_err; |
| |
| return total_err; |
| } |
| |
| static uint64_t pack_mode0_to_one_color(const color_cell_compressor_params * pParams, color_cell_compressor_results * pResults, uint32_t r, uint32_t g, uint32_t b, |
| int * pSelectors, uint32_t num_pixels, const color_quad_i * pPixels) |
| { |
| uint32_t best_err = UINT_MAX; |
| uint32_t best_p = 0; |
| |
| for ( uint32_t p = 0; p < 4; p++) |
| { |
| uint32_t err = g_bc7_mode_0_optimal_endpoints[r][p >> 1][p & 1].m_error + g_bc7_mode_0_optimal_endpoints[g][p >> 1][p & 1].m_error + g_bc7_mode_0_optimal_endpoints[b][p >> 1][p & 1].m_error; |
| if (err < best_err) |
| { |
| best_err = err; |
| best_p = p; |
| } |
| } |
| |
| const endpoint_err *pEr = &g_bc7_mode_0_optimal_endpoints[r][best_p >> 1][best_p & 1]; |
| const endpoint_err *pEg = &g_bc7_mode_0_optimal_endpoints[g][best_p >> 1][best_p & 1]; |
| const endpoint_err *pEb = &g_bc7_mode_0_optimal_endpoints[b][best_p >> 1][best_p & 1]; |
| |
| color_quad_i_set(&pResults->m_low_endpoint, pEr->m_lo, pEg->m_lo, pEb->m_lo, 0); |
| |
| color_quad_i_set(&pResults->m_high_endpoint, pEr->m_hi, pEg->m_hi, pEb->m_hi, 0); |
| |
| pResults->m_pbits[0] = best_p & 1; |
| pResults->m_pbits[1] = best_p >> 1; |
| |
| for ( uint32_t i = 0; i < num_pixels; i++) |
| pSelectors[i] = BC7E_MODE_0_OPTIMAL_INDEX; |
| |
| color_quad_i p; |
| |
| for ( uint32_t i = 0; i < 3; i++) |
| { |
| uint32_t low = ((pResults->m_low_endpoint.m_c[i] << 1) | pResults->m_pbits[0]) << 3; |
| low |= (low >> 5); |
| |
| uint32_t high = ((pResults->m_high_endpoint.m_c[i] << 1) | pResults->m_pbits[1]) << 3; |
| high |= (high >> 5); |
| |
| p.m_c[i] = (low * (64 - g_bc7_weights3[BC7E_MODE_0_OPTIMAL_INDEX]) + high * g_bc7_weights3[BC7E_MODE_0_OPTIMAL_INDEX] + 32) >> 6; |
| } |
| |
| p.m_c[3] = 255; |
| |
| uint64_t total_err = 0; |
| for ( uint32_t i = 0; i < num_pixels; i++) |
| total_err += compute_color_distance_rgb(&p, &pPixels[i], pParams->m_perceptual, pParams->m_weights); |
| |
| pResults->m_best_overall_err = total_err; |
| |
| return total_err; |
| } |
| |
| static uint64_t pack_mode6_to_one_color(const color_cell_compressor_params * pParams, color_cell_compressor_results * pResults, uint32_t r, uint32_t g, uint32_t b, uint32_t a, |
| int * pSelectors, uint32_t num_pixels, const color_quad_i * pPixels) |
| { |
| uint32_t best_err = UINT_MAX; |
| uint32_t best_p = 0; |
| |
| for ( uint32_t p = 0; p < 4; p++) |
| { |
| uint32_t hi_p = p >> 1; |
| uint32_t lo_p = p & 1; |
| uint32_t err = g_bc7_mode_6_optimal_endpoints[r][hi_p][lo_p].m_error + g_bc7_mode_6_optimal_endpoints[g][hi_p][lo_p].m_error + g_bc7_mode_6_optimal_endpoints[b][hi_p][lo_p].m_error + g_bc7_mode_6_optimal_endpoints[a][hi_p][lo_p].m_error; |
| if (err < best_err) |
| { |
| best_err = err; |
| best_p = p; |
| } |
| } |
| |
| uint32_t best_hi_p = best_p >> 1; |
| uint32_t best_lo_p = best_p & 1; |
| |
| const endpoint_err *pEr = &g_bc7_mode_6_optimal_endpoints[r][best_hi_p][best_lo_p]; |
| const endpoint_err *pEg = &g_bc7_mode_6_optimal_endpoints[g][best_hi_p][best_lo_p]; |
| const endpoint_err *pEb = &g_bc7_mode_6_optimal_endpoints[b][best_hi_p][best_lo_p]; |
| const endpoint_err *pEa = &g_bc7_mode_6_optimal_endpoints[a][best_hi_p][best_lo_p]; |
| |
| color_quad_i_set(&pResults->m_low_endpoint, pEr->m_lo, pEg->m_lo, pEb->m_lo, pEa->m_lo); |
| |
| color_quad_i_set(&pResults->m_high_endpoint, pEr->m_hi, pEg->m_hi, pEb->m_hi, pEa->m_hi); |
| |
| pResults->m_pbits[0] = best_lo_p; |
| pResults->m_pbits[1] = best_hi_p; |
| |
| for ( uint32_t i = 0; i < num_pixels; i++) |
| pSelectors[i] = BC7E_MODE_6_OPTIMAL_INDEX; |
| |
| color_quad_i p; |
| |
| for ( uint32_t i = 0; i < 4; i++) |
| { |
| uint32_t low = (pResults->m_low_endpoint.m_c[i] << 1) | pResults->m_pbits[0]; |
| uint32_t high = (pResults->m_high_endpoint.m_c[i] << 1) | pResults->m_pbits[1]; |
| |
| p.m_c[i] = (low * (64 - g_bc7_weights4[BC7E_MODE_6_OPTIMAL_INDEX]) + high * g_bc7_weights4[BC7E_MODE_6_OPTIMAL_INDEX] + 32) >> 6; |
| } |
| |
| uint64_t total_err = 0; |
| for ( uint32_t i = 0; i < num_pixels; i++) |
| total_err += compute_color_distance_rgba(&p, &pPixels[i], pParams->m_perceptual, pParams->m_weights); |
| |
| pResults->m_best_overall_err = total_err; |
| |
| return total_err; |
| } |
| |
| static uint64_t pack_mode7_to_one_color(const color_cell_compressor_params * pParams, color_cell_compressor_results * pResults, uint32_t r, uint32_t g, uint32_t b, uint32_t a, |
| int * pSelectors, uint32_t num_pixels, const color_quad_i * pPixels) |
| { |
| uint32_t best_err = UINT_MAX; |
| uint32_t best_p = 0; |
| |
| for ( uint32_t p = 0; p < 4; p++) |
| { |
| uint32_t hi_p = p >> 1; |
| uint32_t lo_p = p & 1; |
| uint32_t err = g_bc7_mode_7_optimal_endpoints[r][hi_p][lo_p].m_error + g_bc7_mode_7_optimal_endpoints[g][hi_p][lo_p].m_error + g_bc7_mode_7_optimal_endpoints[b][hi_p][lo_p].m_error + g_bc7_mode_7_optimal_endpoints[a][hi_p][lo_p].m_error; |
| if (err < best_err) |
| { |
| best_err = err; |
| best_p = p; |
| } |
| } |
| |
| uint32_t best_hi_p = best_p >> 1; |
| uint32_t best_lo_p = best_p & 1; |
| |
| const endpoint_err *pEr = &g_bc7_mode_7_optimal_endpoints[r][best_hi_p][best_lo_p]; |
| const endpoint_err *pEg = &g_bc7_mode_7_optimal_endpoints[g][best_hi_p][best_lo_p]; |
| const endpoint_err *pEb = &g_bc7_mode_7_optimal_endpoints[b][best_hi_p][best_lo_p]; |
| const endpoint_err *pEa = &g_bc7_mode_7_optimal_endpoints[a][best_hi_p][best_lo_p]; |
| |
| color_quad_i_set(&pResults->m_low_endpoint, pEr->m_lo, pEg->m_lo, pEb->m_lo, pEa->m_lo); |
| |
| color_quad_i_set(&pResults->m_high_endpoint, pEr->m_hi, pEg->m_hi, pEb->m_hi, pEa->m_hi); |
| |
| pResults->m_pbits[0] = best_lo_p; |
| pResults->m_pbits[1] = best_hi_p; |
| |
| for ( uint32_t i = 0; i < num_pixels; i++) |
| pSelectors[i] = BC7E_MODE_7_OPTIMAL_INDEX; |
| |
| color_quad_i p; |
| |
| for ( uint32_t i = 0; i < 4; i++) |
| { |
| uint32_t low = (pResults->m_low_endpoint.m_c[i] << 1) | pResults->m_pbits[0]; |
| uint32_t high = (pResults->m_high_endpoint.m_c[i] << 1) | pResults->m_pbits[1]; |
| |
| p.m_c[i] = (low * (64 - g_bc7_weights2[BC7E_MODE_7_OPTIMAL_INDEX]) + high * g_bc7_weights2[BC7E_MODE_7_OPTIMAL_INDEX] + 32) >> 6; |
| } |
| |
| uint64_t total_err = 0; |
| for ( uint32_t i = 0; i < num_pixels; i++) |
| total_err += compute_color_distance_rgba(&p, &pPixels[i], pParams->m_perceptual, pParams->m_weights); |
| |
| pResults->m_best_overall_err = total_err; |
| |
| return total_err; |
| } |
| |
| static uint64_t evaluate_solution(const color_quad_i * pLow, const color_quad_i * pHigh, const uint32_t * pbits, |
| const color_cell_compressor_params * pParams, color_cell_compressor_results * pResults, uint32_t num_pixels, const color_quad_i * pPixels) |
| { |
| color_quad_i quantMinColor = *pLow; |
| color_quad_i quantMaxColor = *pHigh; |
| |
| if (pParams->m_has_pbits) |
| { |
| uint32_t minPBit, maxPBit; |
| |
| if (pParams->m_endpoints_share_pbit) |
| maxPBit = minPBit = pbits[0]; |
| else |
| { |
| minPBit = pbits[0]; |
| maxPBit = pbits[1]; |
| } |
| |
| quantMinColor.m_c[0] = (pLow->m_c[0] << 1) | minPBit; |
| quantMinColor.m_c[1] = (pLow->m_c[1] << 1) | minPBit; |
| quantMinColor.m_c[2] = (pLow->m_c[2] << 1) | minPBit; |
| quantMinColor.m_c[3] = (pLow->m_c[3] << 1) | minPBit; |
| |
| quantMaxColor.m_c[0] = (pHigh->m_c[0] << 1) | maxPBit; |
| quantMaxColor.m_c[1] = (pHigh->m_c[1] << 1) | maxPBit; |
| quantMaxColor.m_c[2] = (pHigh->m_c[2] << 1) | maxPBit; |
| quantMaxColor.m_c[3] = (pHigh->m_c[3] << 1) | maxPBit; |
| } |
| |
| color_quad_i actualMinColor = scale_color(&quantMinColor, pParams); |
| color_quad_i actualMaxColor = scale_color(&quantMaxColor, pParams); |
| |
| const uint32_t N = pParams->m_num_selector_weights; |
| const uint32_t nc = pParams->m_has_alpha ? 4 : 3; |
| |
| float total_errf = 0; |
| |
| float wr = (float)(pParams->m_weights[0]); |
| float wg = (float)(pParams->m_weights[1]); |
| float wb = (float)(pParams->m_weights[2]); |
| float wa = (float)(pParams->m_weights[3]); |
| |
| color_quad_f weightedColors[16]; |
| weightedColors[0].m_c[0] = (float)(actualMinColor.m_c[0]); |
| weightedColors[0].m_c[1] = (float)(actualMinColor.m_c[1]); |
| weightedColors[0].m_c[2] = (float)(actualMinColor.m_c[2]); |
| weightedColors[0].m_c[3] = (float)(actualMinColor.m_c[3]); |
| |
| weightedColors[N - 1].m_c[0] = (float)(actualMaxColor.m_c[0]); |
| weightedColors[N - 1].m_c[1] = (float)(actualMaxColor.m_c[1]); |
| weightedColors[N - 1].m_c[2] = (float)(actualMaxColor.m_c[2]); |
| weightedColors[N - 1].m_c[3] = (float)(actualMaxColor.m_c[3]); |
| |
| for ( uint32_t i = 1; i < (N - 1); i++) |
| for ( uint32_t j = 0; j < nc; j++) |
| weightedColors[i].m_c[j] = floor((weightedColors[0].m_c[j] * (64.0f - pParams->m_pSelector_weights[i]) + weightedColors[N - 1].m_c[j] * pParams->m_pSelector_weights[i] + 32) * (1.0f / 64.0f)); |
| |
| if (!pParams->m_perceptual) |
| { |
| if (!pParams->m_has_alpha) |
| { |
| if (N == 16) |
| { |
| float lr = (float)(actualMinColor.m_c[0]); |
| float lg = (float)(actualMinColor.m_c[1]); |
| float lb = (float)(actualMinColor.m_c[2]); |
| |
| float dr = actualMaxColor.m_c[0] - lr; |
| float dg = actualMaxColor.m_c[1] - lg; |
| float db = actualMaxColor.m_c[2] - lb; |
| |
| const float f = N / (dr * dr + dg * dg + db * db + BC7E_DENOM_BIAS); |
| |
| lr *= -dr; |
| lg *= -dg; |
| lb *= -db; |
| |
| cfor ( uint32_t i = 0; i < num_pixels; i++) |
| { |
| const color_quad_i * pC = &pPixels[i]; |
| float r = (float)(pC->m_c[0]); |
| float g = (float)(pC->m_c[1]); |
| float b = (float)(pC->m_c[2]); |
| |
| float best_sel = floor(((r * dr + lr) + (g * dg + lg) + (b * db + lb)) * f + .5f); |
| best_sel = clamp(best_sel, (float)1, (float)(N - 1)); |
| |
| float best_sel0 = best_sel - 1; |
| |
| float dr0 = weightedColors[(int)best_sel0].m_c[0] - r; |
| |
| float dg0 = weightedColors[(int)best_sel0].m_c[1] - g; |
| |
| float db0 = weightedColors[(int)best_sel0].m_c[2] - b; |
| |
| float err0 = wr * dr0 * dr0 + wg * dg0 * dg0 + wb * db0 * db0; |
| |
| float dr1 = weightedColors[(int)best_sel].m_c[0] - r; |
| |
| float dg1 = weightedColors[(int)best_sel].m_c[1] - g; |
| |
| float db1 = weightedColors[(int)best_sel].m_c[2] - b; |
| |
| float err1 = wr * dr1 * dr1 + wg * dg1 * dg1 + wb * db1 * db1; |
| |
| float min_err = min(err0, err1); |
| total_errf += min_err; |
| pResults->m_pSelectors_temp[i] = (int)select(min_err == err0, best_sel0, best_sel); |
| } |
| } |
| else if (N == 8) |
| { |
| cfor ( uint32_t i = 0; i < num_pixels; i++) |
| { |
| float pr = (float)pPixels[i].m_c[0]; |
| float pg = (float)pPixels[i].m_c[1]; |
| float pb = (float)pPixels[i].m_c[2]; |
| |
| float best_err; |
| int best_sel; |
| |
| { |
| float dr0 = weightedColors[0].m_c[0] - pr; |
| float dg0 = weightedColors[0].m_c[1] - pg; |
| float db0 = weightedColors[0].m_c[2] - pb; |
| float err0 = wr * dr0 * dr0 + wg * dg0 * dg0 + wb * db0 * db0; |
| |
| float dr1 = weightedColors[1].m_c[0] - pr; |
| float dg1 = weightedColors[1].m_c[1] - pg; |
| float db1 = weightedColors[1].m_c[2] - pb; |
| float err1 = wr * dr1 * dr1 + wg * dg1 * dg1 + wb * db1 * db1; |
| |
| float dr2 = weightedColors[2].m_c[0] - pr; |
| float dg2 = weightedColors[2].m_c[1] - pg; |
| float db2 = weightedColors[2].m_c[2] - pb; |
| float err2 = wr * dr2 * dr2 + wg * dg2 * dg2 + wb * db2 * db2; |
| |
| float dr3 = weightedColors[3].m_c[0] - pr; |
| float dg3 = weightedColors[3].m_c[1] - pg; |
| float db3 = weightedColors[3].m_c[2] - pb; |
| float err3 = wr * dr3 * dr3 + wg * dg3 * dg3 + wb * db3 * db3; |
| |
| best_err = min(min(min(err0, err1), err2), err3); |
| |
| best_sel = select(best_err == err1, 1, 0); |
| best_sel = select(best_err == err2, 2, best_sel); |
| best_sel = select(best_err == err3, 3, best_sel); |
| } |
| |
| { |
| float dr0 = weightedColors[4].m_c[0] - pr; |
| float dg0 = weightedColors[4].m_c[1] - pg; |
| float db0 = weightedColors[4].m_c[2] - pb; |
| float err0 = wr * dr0 * dr0 + wg * dg0 * dg0 + wb * db0 * db0; |
| |
| float dr1 = weightedColors[5].m_c[0] - pr; |
| float dg1 = weightedColors[5].m_c[1] - pg; |
| float db1 = weightedColors[5].m_c[2] - pb; |
| float err1 = wr * dr1 * dr1 + wg * dg1 * dg1 + wb * db1 * db1; |
| |
| float dr2 = weightedColors[6].m_c[0] - pr; |
| float dg2 = weightedColors[6].m_c[1] - pg; |
| float db2 = weightedColors[6].m_c[2] - pb; |
| float err2 = wr * dr2 * dr2 + wg * dg2 * dg2 + wb * db2 * db2; |
| |
| float dr3 = weightedColors[7].m_c[0] - pr; |
| float dg3 = weightedColors[7].m_c[1] - pg; |
| float db3 = weightedColors[7].m_c[2] - pb; |
| float err3 = wr * dr3 * dr3 + wg * dg3 * dg3 + wb * db3 * db3; |
| |
| best_err = min(best_err, min(min(min(err0, err1), err2), err3)); |
| |
| best_sel = select(best_err == err0, 4, best_sel); |
| best_sel = select(best_err == err1, 5, best_sel); |
| best_sel = select(best_err == err2, 6, best_sel); |
| best_sel = select(best_err == err3, 7, best_sel); |
| } |
| |
| total_errf += best_err; |
| |
| pResults->m_pSelectors_temp[i] = best_sel; |
| } |
| } |
| else // if (N == 4) |
| { |
| cfor ( uint32_t i = 0; i < num_pixels; i++) |
| { |
| float pr = (float)pPixels[i].m_c[0]; |
| float pg = (float)pPixels[i].m_c[1]; |
| float pb = (float)pPixels[i].m_c[2]; |
| |
| float dr0 = weightedColors[0].m_c[0] - pr; |
| float dg0 = weightedColors[0].m_c[1] - pg; |
| float db0 = weightedColors[0].m_c[2] - pb; |
| float err0 = wr * dr0 * dr0 + wg * dg0 * dg0 + wb * db0 * db0; |
| |
| float dr1 = weightedColors[1].m_c[0] - pr; |
| float dg1 = weightedColors[1].m_c[1] - pg; |
| float db1 = weightedColors[1].m_c[2] - pb; |
| float err1 = wr * dr1 * dr1 + wg * dg1 * dg1 + wb * db1 * db1; |
| |
| float dr2 = weightedColors[2].m_c[0] - pr; |
| float dg2 = weightedColors[2].m_c[1] - pg; |
| float db2 = weightedColors[2].m_c[2] - pb; |
| float err2 = wr * dr2 * dr2 + wg * dg2 * dg2 + wb * db2 * db2; |
| |
| float dr3 = weightedColors[3].m_c[0] - pr; |
| float dg3 = weightedColors[3].m_c[1] - pg; |
| float db3 = weightedColors[3].m_c[2] - pb; |
| float err3 = wr * dr3 * dr3 + wg * dg3 * dg3 + wb * db3 * db3; |
| |
| float best_err = min(min(min(err0, err1), err2), err3); |
| |
| int best_sel = select(best_err == err1, 1, 0); |
| best_sel = select(best_err == err2, 2, best_sel); |
| best_sel = select(best_err == err3, 3, best_sel); |
| |
| total_errf += best_err; |
| |
| pResults->m_pSelectors_temp[i] = best_sel; |
| } |
| } |
| } |
| else |
| { |
| // alpha |
| if (N == 16) |
| { |
| float lr = (float)(actualMinColor.m_c[0]); |
| float lg = (float)(actualMinColor.m_c[1]); |
| float lb = (float)(actualMinColor.m_c[2]); |
| float la = (float)(actualMinColor.m_c[3]); |
| |
| float dr = actualMaxColor.m_c[0] - lr; |
| float dg = actualMaxColor.m_c[1] - lg; |
| float db = actualMaxColor.m_c[2] - lb; |
| float da = actualMaxColor.m_c[3] - la; |
| |
| const float f = N / (dr * dr + dg * dg + db * db + da * da + BC7E_DENOM_BIAS); |
| |
| lr *= -dr; |
| lg *= -dg; |
| lb *= -db; |
| la *= -da; |
| |
| cfor ( uint32_t i = 0; i < num_pixels; i++) |
| { |
| const color_quad_i * pC = &pPixels[i]; |
| float r = (float)(pC->m_c[0]); |
| float g = (float)(pC->m_c[1]); |
| float b = (float)(pC->m_c[2]); |
| float a = (float)(pC->m_c[3]); |
| |
| float best_sel = floor(((r * dr + lr) + (g * dg + lg) + (b * db + lb) + (a * da + la)) * f + .5f); |
| best_sel = clamp(best_sel, (float)1, (float)(N - 1)); |
| |
| float best_sel0 = best_sel - 1; |
| |
| float dr0 = weightedColors[(int)best_sel0].m_c[0] - r; |
| float dg0 = weightedColors[(int)best_sel0].m_c[1] - g; |
| float db0 = weightedColors[(int)best_sel0].m_c[2] - b; |
| float da0 = weightedColors[(int)best_sel0].m_c[3] - a; |
| float err0 = (wr * dr0 * dr0) + (wg * dg0 * dg0) + (wb * db0 * db0) + (wa * da0 * da0); |
| |
| float dr1 = weightedColors[(int)best_sel].m_c[0] - r; |
| float dg1 = weightedColors[(int)best_sel].m_c[1] - g; |
| float db1 = weightedColors[(int)best_sel].m_c[2] - b; |
| float da1 = weightedColors[(int)best_sel].m_c[3] - a; |
| |
| float err1 = (wr * dr1 * dr1) + (wg * dg1 * dg1) + (wb * db1 * db1) + (wa * da1 * da1); |
| |
| float min_err = min(err0, err1); |
| total_errf += min_err; |
| pResults->m_pSelectors_temp[i] = (int)select(min_err == err0, best_sel0, best_sel); |
| } |
| } |
| else if (N == 8) |
| { |
| cfor ( uint32_t i = 0; i < num_pixels; i++) |
| { |
| float pr = (float)pPixels[i].m_c[0]; |
| float pg = (float)pPixels[i].m_c[1]; |
| float pb = (float)pPixels[i].m_c[2]; |
| float pa = (float)pPixels[i].m_c[3]; |
| |
| float best_err; |
| int best_sel; |
| |
| { |
| float dr0 = weightedColors[0].m_c[0] - pr; |
| float dg0 = weightedColors[0].m_c[1] - pg; |
| float db0 = weightedColors[0].m_c[2] - pb; |
| float da0 = weightedColors[0].m_c[3] - pa; |
| float err0 = wr * dr0 * dr0 + wg * dg0 * dg0 + wb * db0 * db0 + wa * da0 * da0; |
| |
| float dr1 = weightedColors[1].m_c[0] - pr; |
| float dg1 = weightedColors[1].m_c[1] - pg; |
| float db1 = weightedColors[1].m_c[2] - pb; |
| float da1 = weightedColors[1].m_c[3] - pa; |
| float err1 = wr * dr1 * dr1 + wg * dg1 * dg1 + wb * db1 * db1 + wa * da1 * da1; |
| |
| float dr2 = weightedColors[2].m_c[0] - pr; |
| float dg2 = weightedColors[2].m_c[1] - pg; |
| float db2 = weightedColors[2].m_c[2] - pb; |
| float da2 = weightedColors[2].m_c[3] - pa; |
| float err2 = wr * dr2 * dr2 + wg * dg2 * dg2 + wb * db2 * db2 + wa * da2 * da2; |
| |
| float dr3 = weightedColors[3].m_c[0] - pr; |
| float dg3 = weightedColors[3].m_c[1] - pg; |
| float db3 = weightedColors[3].m_c[2] - pb; |
| float da3 = weightedColors[3].m_c[3] - pa; |
| float err3 = wr * dr3 * dr3 + wg * dg3 * dg3 + wb * db3 * db3 + wa * da3 * da3; |
| |
| best_err = min(min(min(err0, err1), err2), err3); |
| |
| best_sel = select(best_err == err1, 1, 0); |
| best_sel = select(best_err == err2, 2, best_sel); |
| best_sel = select(best_err == err3, 3, best_sel); |
| } |
| |
| { |
| float dr0 = weightedColors[4].m_c[0] - pr; |
| float dg0 = weightedColors[4].m_c[1] - pg; |
| float db0 = weightedColors[4].m_c[2] - pb; |
| float da0 = weightedColors[4].m_c[3] - pa; |
| float err0 = wr * dr0 * dr0 + wg * dg0 * dg0 + wb * db0 * db0 + wa * da0 * da0; |
| |
| float dr1 = weightedColors[5].m_c[0] - pr; |
| float dg1 = weightedColors[5].m_c[1] - pg; |
| float db1 = weightedColors[5].m_c[2] - pb; |
| float da1 = weightedColors[5].m_c[3] - pa; |
| float err1 = wr * dr1 * dr1 + wg * dg1 * dg1 + wb * db1 * db1 + wa * da1 * da1; |
| |
| float dr2 = weightedColors[6].m_c[0] - pr; |
| float dg2 = weightedColors[6].m_c[1] - pg; |
| float db2 = weightedColors[6].m_c[2] - pb; |
| float da2 = weightedColors[6].m_c[3] - pa; |
| float err2 = wr * dr2 * dr2 + wg * dg2 * dg2 + wb * db2 * db2 + wa * da2 * da2; |
| |
| float dr3 = weightedColors[7].m_c[0] - pr; |
| float dg3 = weightedColors[7].m_c[1] - pg; |
| float db3 = weightedColors[7].m_c[2] - pb; |
| float da3 = weightedColors[7].m_c[3] - pa; |
| float err3 = wr * dr3 * dr3 + wg * dg3 * dg3 + wb * db3 * db3 + wa * da3 * da3; |
| |
| best_err = min(best_err, min(min(min(err0, err1), err2), err3)); |
| |
| best_sel = select(best_err == err0, 4, best_sel); |
| best_sel = select(best_err == err1, 5, best_sel); |
| best_sel = select(best_err == err2, 6, best_sel); |
| best_sel = select(best_err == err3, 7, best_sel); |
| } |
| |
| total_errf += best_err; |
| |
| pResults->m_pSelectors_temp[i] = best_sel; |
| } |
| } |
| else // if (N == 4) |
| { |
| cfor ( uint32_t i = 0; i < num_pixels; i++) |
| { |
| float pr = (float)pPixels[i].m_c[0]; |
| float pg = (float)pPixels[i].m_c[1]; |
| float pb = (float)pPixels[i].m_c[2]; |
| float pa = (float)pPixels[i].m_c[3]; |
| |
| float dr0 = weightedColors[0].m_c[0] - pr; |
| float dg0 = weightedColors[0].m_c[1] - pg; |
| float db0 = weightedColors[0].m_c[2] - pb; |
| float da0 = weightedColors[0].m_c[3] - pa; |
| float err0 = wr * dr0 * dr0 + wg * dg0 * dg0 + wb * db0 * db0 + wa * da0 * da0; |
| |
| float dr1 = weightedColors[1].m_c[0] - pr; |
| float dg1 = weightedColors[1].m_c[1] - pg; |
| float db1 = weightedColors[1].m_c[2] - pb; |
| float da1 = weightedColors[1].m_c[3] - pa; |
| float err1 = wr * dr1 * dr1 + wg * dg1 * dg1 + wb * db1 * db1 + wa * da1 * da1; |
| |
| float dr2 = weightedColors[2].m_c[0] - pr; |
| float dg2 = weightedColors[2].m_c[1] - pg; |
| float db2 = weightedColors[2].m_c[2] - pb; |
| float da2 = weightedColors[2].m_c[3] - pa; |
| float err2 = wr * dr2 * dr2 + wg * dg2 * dg2 + wb * db2 * db2 + wa * da2 * da2; |
| |
| float dr3 = weightedColors[3].m_c[0] - pr; |
| float dg3 = weightedColors[3].m_c[1] - pg; |
| float db3 = weightedColors[3].m_c[2] - pb; |
| float da3 = weightedColors[3].m_c[3] - pa; |
| float err3 = wr * dr3 * dr3 + wg * dg3 * dg3 + wb * db3 * db3 + wa * da3 * da3; |
| |
| float best_err = min(min(min(err0, err1), err2), err3); |
| |
| int best_sel = select(best_err == err1, 1, 0); |
| best_sel = select(best_err == err2, 2, best_sel); |
| best_sel = select(best_err == err3, 3, best_sel); |
| |
| total_errf += best_err; |
| |
| pResults->m_pSelectors_temp[i] = best_sel; |
| } |
| } |
| } |
| } |
| else |
| { |
| wg *= pr_weight; |
| wb *= pb_weight; |
| |
| float weightedColorsY[16], weightedColorsCr[16], weightedColorsCb[16]; |
| |
| for ( uint32_t i = 0; i < N; i++) |
| { |
| float r = weightedColors[i].m_c[0]; |
| float g = weightedColors[i].m_c[1]; |
| float b = weightedColors[i].m_c[2]; |
| |
| float y = r * .2126f + g * .7152f + b * .0722f; |
| |
| weightedColorsY[i] = y; |
| weightedColorsCr[i] = r - y; |
| weightedColorsCb[i] = b - y; |
| } |
| |
| if (pParams->m_has_alpha) |
| { |
| for ( uint32_t i = 0; i < num_pixels; i++) |
| { |
| float r = (float)(pPixels[i].m_c[0]); |
| float g = (float)(pPixels[i].m_c[1]); |
| float b = (float)(pPixels[i].m_c[2]); |
| float a = (float)(pPixels[i].m_c[3]); |
| |
| float y = r * .2126f + g * .7152f + b * .0722f; |
| float cr = r - y; |
| float cb = b - y; |
| |
| float best_err = 1e+10f; |
| int32_t best_sel = 0; |
| |
| for ( uint32_t j = 0; j < N; j++) |
| { |
| float dl = y - weightedColorsY[j]; |
| float dcr = cr - weightedColorsCr[j]; |
| float dcb = cb - weightedColorsCb[j]; |
| float da = a - weightedColors[j].m_c[3]; |
| |
| float err = (wr * dl * dl) + (wg * dcr * dcr) + (wb * dcb * dcb) + (wa * da * da); |
| if (err < best_err) |
| { |
| best_err = err; |
| best_sel = j; |
| } |
| } |
| |
| total_errf += best_err; |
| |
| pResults->m_pSelectors_temp[i] = best_sel; |
| } |
| } |
| else |
| { |
| for ( uint32_t i = 0; i < num_pixels; i++) |
| { |
| float r = (float)(pPixels[i].m_c[0]); |
| float g = (float)(pPixels[i].m_c[1]); |
| float b = (float)(pPixels[i].m_c[2]); |
| |
| float y = r * .2126f + g * .7152f + b * .0722f; |
| float cr = r - y; |
| float cb = b - y; |
| |
| float best_err = 1e+10f; |
| int32_t best_sel = 0; |
| |
| for ( uint32_t j = 0; j < N; j++) |
| { |
| float dl = y - weightedColorsY[j]; |
| float dcr = cr - weightedColorsCr[j]; |
| float dcb = cb - weightedColorsCb[j]; |
| |
| float err = (wr * dl * dl) + (wg * dcr * dcr) + (wb * dcb * dcb); |
| if (err < best_err) |
| { |
| best_err = err; |
| best_sel = j; |
| } |
| } |
| |
| total_errf += best_err; |
| |
| pResults->m_pSelectors_temp[i] = best_sel; |
| } |
| } |
| } |
| |
| uint64_t total_err = (int64)total_errf; |
| |
| if (total_err < pResults->m_best_overall_err) |
| { |
| pResults->m_best_overall_err = total_err; |
| |
| pResults->m_low_endpoint = *pLow; |
| pResults->m_high_endpoint = *pHigh; |
| |
| pResults->m_pbits[0] = pbits[0]; |
| pResults->m_pbits[1] = pbits[1]; |
| |
| for ( uint32_t i = 0; i < num_pixels; i++) |
| pResults->m_pSelectors[i] = pResults->m_pSelectors_temp[i]; |
| } |
| |
| return total_err; |
| } |
| |
| static void fixDegenerateEndpoints( uint32_t mode, color_quad_i * pTrialMinColor, color_quad_i * pTrialMaxColor, const vec4F * pXl, const vec4F * pXh, uint32_t iscale) |
| { |
| if ((mode == 1) || (mode == 4)) // also mode 2 |
| { |
| // fix degenerate case where the input collapses to a single colorspace voxel, and we loose all freedom (test with grayscale ramps) |
| for ( uint32_t i = 0; i < 3; i++) |
| { |
| if (pTrialMinColor->m_c[i] == pTrialMaxColor->m_c[i]) |
| { |
| if (abs(pXl->m_c[i] - pXh->m_c[i]) > 0.0f) |
| { |
| if (pTrialMinColor->m_c[i] > (int)(iscale >> 1)) |
| { |
| if (pTrialMinColor->m_c[i] > 0) |
| pTrialMinColor->m_c[i]--; |
| else |
| if (pTrialMaxColor->m_c[i] < (int)iscale) |
| pTrialMaxColor->m_c[i]++; |
| } |
| else |
| { |
| if (pTrialMaxColor->m_c[i] < (int)iscale) |
| pTrialMaxColor->m_c[i]++; |
| else if (pTrialMinColor->m_c[i] > 0) |
| pTrialMinColor->m_c[i]--; |
| } |
| |
| if (mode == 4) |
| { |
| if (pTrialMinColor->m_c[i] > (int)(iscale >> 1)) |
| { |
| if (pTrialMaxColor->m_c[i] < (int)iscale) |
| pTrialMaxColor->m_c[i]++; |
| else if (pTrialMinColor->m_c[i] > 0) |
| pTrialMinColor->m_c[i]--; |
| } |
| else |
| { |
| if (pTrialMinColor->m_c[i] > 0) |
| pTrialMinColor->m_c[i]--; |
| else if (pTrialMaxColor->m_c[i] < (int)iscale) |
| pTrialMaxColor->m_c[i]++; |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| static uint64_t find_optimal_solution( uint32_t mode, vec4F * pXl, vec4F * pXh, const color_cell_compressor_params * pParams, color_cell_compressor_results * pResults, |
| bool pbit_search, uint32_t num_pixels, const color_quad_i * pPixels) |
| { |
| vec4F xl = *pXl; |
| vec4F xh = *pXh; |
| |
| vec4F_saturate_in_place(&xl); |
| vec4F_saturate_in_place(&xh); |
| |
| if (pParams->m_has_pbits) |
| { |
| if (pbit_search) |
| { |
| // compensated rounding+pbit search |
| const int iscalep = (1 << (pParams->m_comp_bits + 1)) - 1; |
| const float scalep = (float)iscalep; |
| |
| const int32_t totalComps = pParams->m_has_alpha ? 4 : 3; |
| NOTE_UNUSED(totalComps); |
| |
| if (!pParams->m_endpoints_share_pbit) |
| { |
| color_quad_i lo[2], hi[2]; |
| |
| for ( int p = 0; p < 2; p++) |
| { |
| color_quad_i xMinColor, xMaxColor; |
| |
| // Notes: The pbit controls which quantization intervals are selected. |
| // total_levels=2^(comp_bits+1), where comp_bits=4 for mode 0, etc. |
| // pbit 0: v=(b*2)/(total_levels-1), pbit 1: v=(b*2+1)/(total_levels-1) where b is the component bin from [0,total_levels/2-1] and v is the [0,1] component value |
| // rearranging you get for pbit 0: b=floor(v*(total_levels-1)/2+.5) |
| // rearranging you get for pbit 1: b=floor((v*(total_levels-1)-1)/2+.5) |
| for ( uint32_t c = 0; c < 4; c++) |
| { |
| xMinColor.m_c[c] = (int)((xl.m_c[c] * scalep - p) / 2.0f + .5f) * 2 + p; |
| xMinColor.m_c[c] = clamp(xMinColor.m_c[c], p, iscalep - 1 + p); |
| |
| xMaxColor.m_c[c] = (int)((xh.m_c[c] * scalep - p) / 2.0f + .5f) * 2 + p; |
| xMaxColor.m_c[c] = clamp(xMaxColor.m_c[c], p, iscalep - 1 + p); |
| } |
| |
| lo[p] = xMinColor; |
| hi[p] = xMaxColor; |
| |
| for ( int c = 0; c < 4; c++) |
| { |
| lo[p].m_c[c] >>= 1; |
| hi[p].m_c[c] >>= 1; |
| } |
| } |
| |
| fixDegenerateEndpoints(mode, &lo[0], &hi[0], &xl, &xh, iscalep >> 1); |
| fixDegenerateEndpoints(mode, &lo[1], &hi[1], &xl, &xh, iscalep >> 1); |
| |
| uint32_t pbits[2]; |
| |
| pbits[0] = 0; pbits[1] = 0; |
| evaluate_solution(&lo[0], &hi[0], pbits, pParams, pResults, num_pixels, pPixels); |
| |
| pbits[0] = 0; pbits[1] = 1; |
| evaluate_solution(&lo[0], &hi[1], pbits, pParams, pResults, num_pixels, pPixels); |
| |
| pbits[0] = 1; pbits[1] = 0; |
| evaluate_solution(&lo[1], &hi[0], pbits, pParams, pResults, num_pixels, pPixels); |
| |
| pbits[0] = 1; pbits[1] = 1; |
| evaluate_solution(&lo[1], &hi[1], pbits, pParams, pResults, num_pixels, pPixels); |
| } |
| else |
| { |
| // Endpoints share pbits |
| color_quad_i lo[2], hi[2]; |
| |
| for ( int p = 0; p < 2; p++) |
| { |
| color_quad_i xMinColor, xMaxColor; |
| |
| for ( uint32_t c = 0; c < 4; c++) |
| { |
| xMinColor.m_c[c] = (int)((xl.m_c[c] * scalep - p) / 2.0f + .5f) * 2 + p; |
| xMinColor.m_c[c] = clamp(xMinColor.m_c[c], p, iscalep - 1 + p); |
| |
| xMaxColor.m_c[c] = (int)((xh.m_c[c] * scalep - p) / 2.0f + .5f) * 2 + p; |
| xMaxColor.m_c[c] = clamp(xMaxColor.m_c[c], p, iscalep - 1 + p); |
| } |
| |
| lo[p] = xMinColor; |
| hi[p] = xMaxColor; |
| |
| for ( int c = 0; c < 4; c++) |
| { |
| lo[p].m_c[c] >>= 1; |
| hi[p].m_c[c] >>= 1; |
| } |
| } |
| |
| fixDegenerateEndpoints(mode, &lo[0], &hi[0], &xl, &xh, iscalep >> 1); |
| fixDegenerateEndpoints(mode, &lo[1], &hi[1], &xl, &xh, iscalep >> 1); |
| |
| uint32_t pbits[2]; |
| |
| pbits[0] = 0; pbits[1] = 0; |
| evaluate_solution(&lo[0], &hi[0], pbits, pParams, pResults, num_pixels, pPixels); |
| |
| pbits[0] = 1; pbits[1] = 1; |
| evaluate_solution(&lo[1], &hi[1], pbits, pParams, pResults, num_pixels, pPixels); |
| } |
| } |
| else |
| { |
| // compensated rounding |
| const int iscalep = (1 << (pParams->m_comp_bits + 1)) - 1; |
| const float scalep = (float)iscalep; |
| |
| const int32_t totalComps = pParams->m_has_alpha ? 4 : 3; |
| |
| uint32_t best_pbits[2]; |
| color_quad_i bestMinColor, bestMaxColor; |
| |
| if (!pParams->m_endpoints_share_pbit) |
| { |
| float best_err0 = 1e+9; |
| float best_err1 = 1e+9; |
| |
| for ( int p = 0; p < 2; p++) |
| { |
| color_quad_i xMinColor, xMaxColor; |
| |
| // Notes: The pbit controls which quantization intervals are selected. |
| // total_levels=2^(comp_bits+1), where comp_bits=4 for mode 0, etc. |
| // pbit 0: v=(b*2)/(total_levels-1), pbit 1: v=(b*2+1)/(total_levels-1) where b is the component bin from [0,total_levels/2-1] and v is the [0,1] component value |
| // rearranging you get for pbit 0: b=floor(v*(total_levels-1)/2+.5) |
| // rearranging you get for pbit 1: b=floor((v*(total_levels-1)-1)/2+.5) |
| for ( uint32_t c = 0; c < 4; c++) |
| { |
| xMinColor.m_c[c] = (int)((xl.m_c[c] * scalep - p) / 2.0f + .5f) * 2 + p; |
| xMinColor.m_c[c] = clamp(xMinColor.m_c[c], p, iscalep - 1 + p); |
| |
| xMaxColor.m_c[c] = (int)((xh.m_c[c] * scalep - p) / 2.0f + .5f) * 2 + p; |
| xMaxColor.m_c[c] = clamp(xMaxColor.m_c[c], p, iscalep - 1 + p); |
| } |
| |
| color_quad_i scaledLow = scale_color(&xMinColor, pParams); |
| color_quad_i scaledHigh = scale_color(&xMaxColor, pParams); |
| |
| float err0 = 0; |
| float err1 = 0; |
| for ( int i = 0; i < totalComps; i++) |
| { |
| err0 += square(scaledLow.m_c[i] - xl.m_c[i]*255.0f); |
| err1 += square(scaledHigh.m_c[i] - xh.m_c[i]*255.0f); |
| } |
| |
| if (err0 < best_err0) |
| { |
| best_err0 = err0; |
| best_pbits[0] = p; |
| |
| bestMinColor.m_c[0] = xMinColor.m_c[0] >> 1; |
| bestMinColor.m_c[1] = xMinColor.m_c[1] >> 1; |
| bestMinColor.m_c[2] = xMinColor.m_c[2] >> 1; |
| bestMinColor.m_c[3] = xMinColor.m_c[3] >> 1; |
| } |
| |
| if (err1 < best_err1) |
| { |
| best_err1 = err1; |
| best_pbits[1] = p; |
| |
| bestMaxColor.m_c[0] = xMaxColor.m_c[0] >> 1; |
| bestMaxColor.m_c[1] = xMaxColor.m_c[1] >> 1; |
| bestMaxColor.m_c[2] = xMaxColor.m_c[2] >> 1; |
| bestMaxColor.m_c[3] = xMaxColor.m_c[3] >> 1; |
| } |
| } |
| } |
| else |
| { |
| // Endpoints share pbits |
| float best_err = 1e+9; |
| |
| for ( int p = 0; p < 2; p++) |
| { |
| color_quad_i xMinColor, xMaxColor; |
| |
| for ( uint32_t c = 0; c < 4; c++) |
| { |
| xMinColor.m_c[c] = (int)((xl.m_c[c] * scalep - p) / 2.0f + .5f) * 2 + p; |
| xMinColor.m_c[c] = clamp(xMinColor.m_c[c], p, iscalep - 1 + p); |
| |
| xMaxColor.m_c[c] = (int)((xh.m_c[c] * scalep - p) / 2.0f + .5f) * 2 + p; |
| xMaxColor.m_c[c] = clamp(xMaxColor.m_c[c], p, iscalep - 1 + p); |
| } |
| |
| color_quad_i scaledLow = scale_color(&xMinColor, pParams); |
| color_quad_i scaledHigh = scale_color(&xMaxColor, pParams); |
| |
| float err = 0; |
| for ( int i = 0; i < totalComps; i++) |
| err += square((scaledLow.m_c[i]/255.0f) - xl.m_c[i]) + square((scaledHigh.m_c[i]/255.0f) - xh.m_c[i]); |
| |
| if (err < best_err) |
| { |
| best_err = err; |
| best_pbits[0] = p; |
| best_pbits[1] = p; |
| |
| bestMinColor.m_c[0] = xMinColor.m_c[0] >> 1; |
| bestMinColor.m_c[1] = xMinColor.m_c[1] >> 1; |
| bestMinColor.m_c[2] = xMinColor.m_c[2] >> 1; |
| bestMinColor.m_c[3] = xMinColor.m_c[3] >> 1; |
| |
| bestMaxColor.m_c[0] = xMaxColor.m_c[0] >> 1; |
| bestMaxColor.m_c[1] = xMaxColor.m_c[1] >> 1; |
| bestMaxColor.m_c[2] = xMaxColor.m_c[2] >> 1; |
| bestMaxColor.m_c[3] = xMaxColor.m_c[3] >> 1; |
| } |
| } |
| } |
| |
| fixDegenerateEndpoints(mode, &bestMinColor, &bestMaxColor, &xl, &xh, iscalep >> 1); |
| |
| if ((pResults->m_best_overall_err == UINT64_MAX) || color_quad_i_notequals(&bestMinColor, &pResults->m_low_endpoint) || color_quad_i_notequals(&bestMaxColor, &pResults->m_high_endpoint) || (best_pbits[0] != pResults->m_pbits[0]) || (best_pbits[1] != pResults->m_pbits[1])) |
| { |
| evaluate_solution(&bestMinColor, &bestMaxColor, best_pbits, pParams, pResults, num_pixels, pPixels); |
| } |
| } |
| } |
| else |
| { |
| const int iscale = (1 << pParams->m_comp_bits) - 1; |
| const float scale = (float)iscale; |
| |
| color_quad_i trialMinColor, trialMaxColor; |
| color_quad_i_set_clamped(&trialMinColor, (int)(xl.m_c[0] * scale + .5f), (int)(xl.m_c[1] * scale + .5f), (int)(xl.m_c[2] * scale + .5f), (int)(xl.m_c[3] * scale + .5f)); |
| color_quad_i_set_clamped(&trialMaxColor, (int)(xh.m_c[0] * scale + .5f), (int)(xh.m_c[1] * scale + .5f), (int)(xh.m_c[2] * scale + .5f), (int)(xh.m_c[3] * scale + .5f)); |
| |
| fixDegenerateEndpoints(mode, &trialMinColor, &trialMaxColor, &xl, &xh, iscale); |
| |
| if ((pResults->m_best_overall_err == UINT64_MAX) || color_quad_i_notequals(&trialMinColor, &pResults->m_low_endpoint) || color_quad_i_notequals(&trialMaxColor, &pResults->m_high_endpoint)) |
| { |
| uint32_t pbits[2]; |
| pbits[0] = 0; |
| pbits[1] = 0; |
| |
| evaluate_solution(&trialMinColor, &trialMaxColor, pbits, pParams, pResults, num_pixels, pPixels); |
| } |
| } |
| |
| return pResults->m_best_overall_err; |
| } |
| |
| // Note: In mode 6, m_has_alpha will only be true for transparent blocks. |
| static uint64_t color_cell_compression( uint32_t mode, const color_cell_compressor_params * pParams, color_cell_compressor_results * pResults, |
| const bc7e_compress_block_params * pComp_params, uint32_t num_pixels, const color_quad_i * pPixels, bool refinement) |
| { |
| pResults->m_best_overall_err = UINT64_MAX; |
| pResults->m_used_lut = false; // set true below only if a one-color LUT result wins |
| |
| if ((mode != 6) && (mode != 7)) |
| { |
| assert(!pParams->m_has_alpha); |
| } |
| |
| if ((mode <= 2) || (mode == 4) || (mode >= 6)) |
| { |
| const uint32_t cr = pPixels[0].m_c[0]; |
| const uint32_t cg = pPixels[0].m_c[1]; |
| const uint32_t cb = pPixels[0].m_c[2]; |
| const uint32_t ca = pPixels[0].m_c[3]; |
| |
| bool allSame = true; |
| for ( uint32_t i = 1; i < num_pixels; i++) |
| { |
| if ((cr != (uint32_t)pPixels[i].m_c[0]) || (cg != (uint32_t)pPixels[i].m_c[1]) || (cb != (uint32_t)pPixels[i].m_c[2]) || (ca != (uint32_t)pPixels[i].m_c[3])) |
| { |
| allSame = false; |
| break; |
| } |
| } |
| |
| cif (allSame && pComp_params->m_use_luts) |
| { |
| pResults->m_used_lut = true; // solid/allSame subset -> one-color optimal-endpoint LUT |
| if (mode == 0) |
| return pack_mode0_to_one_color(pParams, pResults, cr, cg, cb, pResults->m_pSelectors, num_pixels, pPixels); |
| if (mode == 1) |
| return pack_mode1_to_one_color(pParams, pResults, cr, cg, cb, pResults->m_pSelectors, num_pixels, pPixels); |
| else if (mode == 6) |
| return pack_mode6_to_one_color(pParams, pResults, cr, cg, cb, ca, pResults->m_pSelectors, num_pixels, pPixels); |
| else if (mode == 7) |
| return pack_mode7_to_one_color(pParams, pResults, cr, cg, cb, ca, pResults->m_pSelectors, num_pixels, pPixels); |
| else |
| return pack_mode24_to_one_color(pParams, pResults, cr, cg, cb, pResults->m_pSelectors, num_pixels, pPixels); |
| } |
| } |
| |
| vec4F meanColor, axis; |
| vec4F_set_scalar(&meanColor, 0.0f); |
| |
| for ( uint32_t i = 0; i < num_pixels; i++) |
| { |
| vec4F color = vec4F_from_color(&pPixels[i]); |
| meanColor = vec4F_add(&meanColor, &color); |
| } |
| |
| vec4F meanColorScaled = vec4F_mul(&meanColor, 1.0f / (float)((int)num_pixels)); |
| |
| meanColor = vec4F_mul(&meanColor, 1.0f / (float)((int)num_pixels * 255.0f)); |
| vec4F_saturate_in_place(&meanColor); |
| |
| if (pParams->m_has_alpha) |
| { |
| vec4F v; |
| vec4F_set_scalar(&v, 0.0f); |
| cfor ( uint32_t i = 0; i < num_pixels; i++) |
| { |
| vec4F color = vec4F_from_color(&pPixels[i]); |
| color = vec4F_sub(&color, &meanColorScaled); |
| |
| vec4F a = vec4F_mul(&color, color.m_c[0]); |
| vec4F b = vec4F_mul(&color, color.m_c[1]); |
| vec4F c = vec4F_mul(&color, color.m_c[2]); |
| vec4F d = vec4F_mul(&color, color.m_c[3]); |
| |
| vec4F n = i ? v : color; |
| vec4F_normalize_in_place(&n); |
| |
| v.m_c[0] += vec4F_dot(&a, &n); |
| v.m_c[1] += vec4F_dot(&b, &n); |
| v.m_c[2] += vec4F_dot(&c, &n); |
| v.m_c[3] += vec4F_dot(&d, &n); |
| } |
| axis = v; |
| vec4F_normalize_in_place(&axis); |
| } |
| else |
| { |
| float cov[6]; |
| cov[0] = 0; cov[1] = 0; cov[2] = 0; |
| cov[3] = 0; cov[4] = 0; cov[5] = 0; |
| |
| cfor ( uint32_t i = 0; i < num_pixels; i++) |
| { |
| const color_quad_i * pV = &pPixels[i]; |
| |
| float r = pV->m_c[0] - meanColorScaled.m_c[0]; |
| float g = pV->m_c[1] - meanColorScaled.m_c[1]; |
| float b = pV->m_c[2] - meanColorScaled.m_c[2]; |
| |
| cov[0] += r*r; |
| cov[1] += r*g; |
| cov[2] += r*b; |
| cov[3] += g*g; |
| cov[4] += g*b; |
| cov[5] += b*b; |
| } |
| |
| float vfr, vfg, vfb; |
| //vfr = hi[0] - lo[0]; |
| //vfg = hi[1] - lo[1]; |
| //vfb = hi[2] - lo[2]; |
| // This is more stable. |
| vfr = .9f; |
| vfg = 1.0f; |
| vfb = .7f; |
| |
| for ( uint32_t iter = 0; iter < 3; iter++) |
| { |
| float r = vfr*cov[0] + vfg*cov[1] + vfb*cov[2]; |
| float g = vfr*cov[1] + vfg*cov[3] + vfb*cov[4]; |
| float b = vfr*cov[2] + vfg*cov[4] + vfb*cov[5]; |
| |
| float m = maximumf(maximumf(abs(r), abs(g)), abs(b)); |
| if (m > 1e-10f) |
| { |
| m = 1.0f / m; |
| r *= m; |
| g *= m; |
| b *= m; |
| } |
| |
| //float delta = square(vfr - r) + square(vfg - g) + square(vfb - b); |
| |
| vfr = r; |
| vfg = g; |
| vfb = b; |
| |
| //if ((iter > 1) && (delta < 1e-8f)) |
| // break; |
| } |
| |
| float len = vfr*vfr + vfg*vfg + vfb*vfb; |
| |
| if (len < 1e-10f) |
| vec4F_set_scalar(&axis, 0.0f); |
| else |
| { |
| len = 1.0f / sqrt(len); |
| vfr *= len; |
| vfg *= len; |
| vfb *= len; |
| vec4F_set(&axis, vfr, vfg, vfb, 0); |
| } |
| } |
| |
| cif (vec4F_dot(&axis, &axis) < .5f) |
| { |
| if (pParams->m_perceptual) |
| vec4F_set(&axis, .213f, .715f, .072f, pParams->m_has_alpha ? .715f : 0); |
| else |
| vec4F_set(&axis, 1.0f, 1.0f, 1.0f, pParams->m_has_alpha ? 1.0f : 0); |
| vec4F_normalize_in_place(&axis); |
| } |
| |
| float l = 1e+9f, h = -1e+9f; |
| |
| cfor ( uint32_t i = 0; i < num_pixels; i++) |
| { |
| vec4F color = vec4F_from_color(&pPixels[i]); |
| |
| vec4F q = vec4F_sub(&color, &meanColorScaled); |
| float d = vec4F_dot(&q, &axis); |
| |
| l = minimumf(l, d); |
| h = maximumf(h, d); |
| } |
| |
| l *= (1.0f / 255.0f); |
| h *= (1.0f / 255.0f); |
| |
| vec4F b0 = vec4F_mul(&axis, l); |
| vec4F b1 = vec4F_mul(&axis, h); |
| vec4F c0 = vec4F_add(&meanColor, &b0); |
| vec4F c1 = vec4F_add(&meanColor, &b1); |
| vec4F minColor = vec4F_saturate(&c0); |
| vec4F maxColor = vec4F_saturate(&c1); |
| |
| vec4F whiteVec; |
| vec4F_set_scalar(&whiteVec, 1.0f); |
| if (vec4F_dot(&minColor, &whiteVec) > vec4F_dot(&maxColor, &whiteVec)) |
| { |
| vec4F temp = minColor; |
| minColor = maxColor; |
| maxColor = temp; |
| } |
| |
| if (!find_optimal_solution(mode, &minColor, &maxColor, pParams, pResults, pComp_params->m_pbit_search, num_pixels, pPixels)) |
| return 0; |
| |
| if (!refinement) |
| return pResults->m_best_overall_err; |
| |
| for ( uint32_t i = 0; i < pComp_params->m_refinement_passes; i++) |
| { |
| vec4F xl, xh; |
| vec4F_set_scalar(&xl, 0.0f); |
| vec4F_set_scalar(&xh, 0.0f); |
| if (pParams->m_has_alpha) |
| compute_least_squares_endpoints_rgba(num_pixels, pResults->m_pSelectors, pParams->m_pSelector_weightsx, &xl, &xh, pPixels); |
| else |
| { |
| compute_least_squares_endpoints_rgb(num_pixels, pResults->m_pSelectors, pParams->m_pSelector_weightsx, &xl, &xh, pPixels); |
| |
| xl.m_c[3] = 255.0f; |
| xh.m_c[3] = 255.0f; |
| } |
| |
| xl = vec4F_mul(&xl, (1.0f / 255.0f)); |
| xh = vec4F_mul(&xh, (1.0f / 255.0f)); |
| |
| if (!find_optimal_solution(mode, &xl, &xh, pParams, pResults, pComp_params->m_pbit_search, num_pixels, pPixels)) |
| return 0; |
| } |
| |
| if (pComp_params->m_uber_level > 0) |
| { |
| int selectors_temp[16], selectors_temp1[16]; |
| for ( uint32_t i = 0; i < num_pixels; i++) |
| selectors_temp[i] = pResults->m_pSelectors[i]; |
| |
| const int max_selector = pParams->m_num_selector_weights - 1; |
| |
| uint32_t min_sel = 16; |
| uint32_t max_sel = 0; |
| for ( uint32_t i = 0; i < num_pixels; i++) |
| { |
| uint32_t sel = selectors_temp[i]; |
| min_sel = minimumu(min_sel, sel); |
| max_sel = maximumu(max_sel, sel); |
| } |
| |
| vec4F xl, xh; |
| vec4F_set_scalar(&xl, 0.0f); |
| vec4F_set_scalar(&xh, 0.0f); |
| |
| if (pComp_params->m_uber1_mask & 1) |
| { |
| for ( uint32_t i = 0; i < num_pixels; i++) |
| { |
| uint32_t sel = selectors_temp[i]; |
| if ((sel == min_sel) && (sel < (pParams->m_num_selector_weights - 1))) |
| sel++; |
| selectors_temp1[i] = sel; |
| } |
| |
| if (pParams->m_has_alpha) |
| compute_least_squares_endpoints_rgba(num_pixels, selectors_temp1, pParams->m_pSelector_weightsx, &xl, &xh, pPixels); |
| else |
| { |
| compute_least_squares_endpoints_rgb(num_pixels, selectors_temp1, pParams->m_pSelector_weightsx, &xl, &xh, pPixels); |
| xl.m_c[3] = 255.0f; |
| xh.m_c[3] = 255.0f; |
| } |
| |
| xl = vec4F_mul(&xl, (1.0f / 255.0f)); |
| xh = vec4F_mul(&xh, (1.0f / 255.0f)); |
| |
| if (!find_optimal_solution(mode, &xl, &xh, pParams, pResults, pComp_params->m_pbit_search, num_pixels, pPixels)) |
| return 0; |
| } |
| |
| if (pComp_params->m_uber1_mask & 2) |
| { |
| for ( uint32_t i = 0; i < num_pixels; i++) |
| { |
| uint32_t sel = selectors_temp[i]; |
| if ((sel == max_sel) && (sel > 0)) |
| sel--; |
| selectors_temp1[i] = sel; |
| } |
| |
| if (pParams->m_has_alpha) |
| compute_least_squares_endpoints_rgba(num_pixels, selectors_temp1, pParams->m_pSelector_weightsx, &xl, &xh, pPixels); |
| else |
| { |
| compute_least_squares_endpoints_rgb(num_pixels, selectors_temp1, pParams->m_pSelector_weightsx, &xl, &xh, pPixels); |
| xl.m_c[3] = 255.0f; |
| xh.m_c[3] = 255.0f; |
| } |
| |
| xl = vec4F_mul(&xl, (1.0f / 255.0f)); |
| xh = vec4F_mul(&xh, (1.0f / 255.0f)); |
| |
| if (!find_optimal_solution(mode, &xl, &xh, pParams, pResults, pComp_params->m_pbit_search, num_pixels, pPixels)) |
| return 0; |
| } |
| |
| if (pComp_params->m_uber1_mask & 4) |
| { |
| for ( uint32_t i = 0; i < num_pixels; i++) |
| { |
| uint32_t sel = selectors_temp[i]; |
| if ((sel == min_sel) && (sel < (pParams->m_num_selector_weights - 1))) |
| sel++; |
| else if ((sel == max_sel) && (sel > 0)) |
| sel--; |
| selectors_temp1[i] = sel; |
| } |
| |
| if (pParams->m_has_alpha) |
| compute_least_squares_endpoints_rgba(num_pixels, selectors_temp1, pParams->m_pSelector_weightsx, &xl, &xh, pPixels); |
| else |
| { |
| compute_least_squares_endpoints_rgb(num_pixels, selectors_temp1, pParams->m_pSelector_weightsx, &xl, &xh, pPixels); |
| xl.m_c[3] = 255.0f; |
| xh.m_c[3] = 255.0f; |
| } |
| |
| xl = vec4F_mul(&xl, (1.0f / 255.0f)); |
| xh = vec4F_mul(&xh, (1.0f / 255.0f)); |
| |
| if (!find_optimal_solution(mode, &xl, &xh, pParams, pResults, pComp_params->m_pbit_search, num_pixels, pPixels)) |
| return 0; |
| } |
| |
| const uint32_t uber_err_thresh = (num_pixels * 56) >> 4; |
| if ((pComp_params->m_uber_level >= 2) && (pResults->m_best_overall_err > uber_err_thresh)) |
| { |
| const int Q = (pComp_params->m_uber_level >= 4) ? (pComp_params->m_uber_level - 2) : 1; |
| for ( int ly = -Q; ly <= 1; ly++) |
| { |
| for ( int hy = max_selector - 1; hy <= (max_selector + Q); hy++) |
| { |
| if ((ly == 0) && (hy == max_selector)) |
| continue; |
| |
| for ( uint32_t i = 0; i < num_pixels; i++) |
| selectors_temp1[i] = (int)clampf(floor((float)max_selector * ((float)(int)selectors_temp[i] - (float)ly) / ((float)hy - (float)ly) + .5f), 0, (float)max_selector); |
| |
| vec4F_set_scalar(&xl, 0.0f); |
| vec4F_set_scalar(&xh, 0.0f); |
| if (pParams->m_has_alpha) |
| compute_least_squares_endpoints_rgba(num_pixels, selectors_temp1, pParams->m_pSelector_weightsx, &xl, &xh, pPixels); |
| else |
| { |
| compute_least_squares_endpoints_rgb(num_pixels, selectors_temp1, pParams->m_pSelector_weightsx, &xl, &xh, pPixels); |
| xl.m_c[3] = 255.0f; |
| xh.m_c[3] = 255.0f; |
| } |
| |
| xl = vec4F_mul(&xl, (1.0f / 255.0f)); |
| xh = vec4F_mul(&xh, (1.0f / 255.0f)); |
| |
| if (!find_optimal_solution(mode, &xl, &xh, pParams, pResults, pComp_params->m_pbit_search && (pComp_params->m_uber_level >= 2), num_pixels, pPixels)) |
| return 0; |
| } |
| } |
| } |
| } |
| |
| if (((mode <= 2) || (mode == 4) || (mode >= 6)) && pComp_params->m_use_luts) |
| { |
| color_cell_compressor_results avg_results; |
| memset(&avg_results, 0, sizeof(avg_results)); |
| |
| avg_results.m_best_overall_err = pResults->m_best_overall_err; |
| avg_results.m_pSelectors = pResults->m_pSelectors; |
| avg_results.m_pSelectors_temp = pResults->m_pSelectors_temp; |
| |
| const uint32_t r = (int)(.5f + meanColor.m_c[0] * 255.0f); |
| const uint32_t g = (int)(.5f + meanColor.m_c[1] * 255.0f); |
| const uint32_t b = (int)(.5f + meanColor.m_c[2] * 255.0f); |
| const uint32_t a = (int)(.5f + meanColor.m_c[3] * 255.0f); |
| |
| uint64_t avg_err; |
| if (mode == 0) |
| avg_err = pack_mode0_to_one_color(pParams, &avg_results, r, g, b, pResults->m_pSelectors_temp, num_pixels, pPixels); |
| else if (mode == 1) |
| avg_err = pack_mode1_to_one_color(pParams, &avg_results, r, g, b, pResults->m_pSelectors_temp, num_pixels, pPixels); |
| else if (mode == 6) |
| avg_err = pack_mode6_to_one_color(pParams, &avg_results, r, g, b, a, pResults->m_pSelectors_temp, num_pixels, pPixels); |
| else if (mode == 7) |
| avg_err = pack_mode7_to_one_color(pParams, &avg_results, r, g, b, a, pResults->m_pSelectors_temp, num_pixels, pPixels); |
| else |
| avg_err = pack_mode24_to_one_color(pParams, &avg_results, r, g, b, pResults->m_pSelectors_temp, num_pixels, pPixels); |
| |
| if (avg_err < pResults->m_best_overall_err) |
| { |
| pResults->m_best_overall_err = avg_err; |
| pResults->m_low_endpoint = avg_results.m_low_endpoint; |
| pResults->m_high_endpoint = avg_results.m_high_endpoint; |
| pResults->m_pbits[0] = avg_results.m_pbits[0]; |
| pResults->m_pbits[1] = avg_results.m_pbits[1]; |
| |
| for ( uint32_t i = 0; i < num_pixels; i++) |
| pResults->m_pSelectors[i] = pResults->m_pSelectors_temp[i]; |
| |
| pResults->m_used_lut = true; // average-color one-color LUT candidate won |
| } |
| } |
| |
| return pResults->m_best_overall_err; |
| } |
| |
| static uint64_t color_cell_compression_est( uint32_t mode, const color_cell_compressor_params * pParams, uint64_t best_err_so_far, uint32_t num_pixels, const color_quad_i * pPixels) |
| { |
| NOTE_UNUSED(best_err_so_far); |
| assert((pParams->m_num_selector_weights == 4) || (pParams->m_num_selector_weights == 8)); |
| |
| float lr = 255, lg = 255, lb = 255; |
| float hr = 0, hg = 0, hb = 0; |
| for ( uint32_t i = 0; i < num_pixels; i++) |
| { |
| const color_quad_i * pC = &pPixels[i]; |
| |
| float r = (float)(pC->m_c[0]); |
| float g = (float)(pC->m_c[1]); |
| float b = (float)(pC->m_c[2]); |
| |
| lr = min(lr, r); |
| lg = min(lg, g); |
| lb = min(lb, b); |
| |
| hr = max(hr, r); |
| hg = max(hg, g); |
| hb = max(hb, b); |
| } |
| |
| const uint32_t N = 1 << g_bc7_color_index_bitcount[mode]; |
| |
| uint64_t total_err = 0; |
| |
| float sr = lr; |
| float sg = lg; |
| float sb = lb; |
| |
| float dir = hr - lr; |
| float dig = hg - lg; |
| float dib = hb - lb; |
| |
| float far = dir; |
| float fag = dig; |
| float fab = dib; |
| |
| float low = far * sr + fag * sg + fab * sb; |
| float high = far * hr + fag * hg + fab * hb; |
| |
| float scale = ((float)N - 1) / ((float)(high - low) + BC7E_DENOM_BIAS); |
| float inv_n = 1.0f / ((float)N - 1); |
| |
| float total_errf = 0; |
| |
| // We don't handle perceptual very well here, but the difference is very slight (<.05 dB avg Luma PSNR across a large corpus) and the perf lost was high (2x slower). |
| if ((pParams->m_weights[0] != 1) || (pParams->m_weights[1] != 1) || (pParams->m_weights[2] != 1)) |
| { |
| float wr = (float)(pParams->m_weights[0]); |
| float wg = (float)(pParams->m_weights[1]); |
| float wb = (float)(pParams->m_weights[2]); |
| |
| for ( uint32_t i = 0; i < num_pixels; i++) |
| { |
| const color_quad_i * pC = &pPixels[i]; |
| |
| float d = far * (float)pC->m_c[0] + fag * (float)pC->m_c[1] + fab * (float)pC->m_c[2]; |
| |
| float s = clamp(floorf((d - low) * scale + .5f) * inv_n, 0.0f, 1.0f); |
| |
| float itr = sr + dir * s; |
| float itg = sg + dig * s; |
| float itb = sb + dib * s; |
| |
| float dr = itr - (float)pC->m_c[0]; |
| float dg = itg - (float)pC->m_c[1]; |
| float db = itb - (float)pC->m_c[2]; |
| |
| total_errf += wr * dr * dr + wg * dg * dg + wb * db * db; |
| } |
| } |
| else |
| { |
| for ( uint32_t i = 0; i < num_pixels; i++) |
| { |
| const color_quad_i * pC = &pPixels[i]; |
| |
| float d = far * (float)pC->m_c[0] + fag * (float)pC->m_c[1] + fab * (float)pC->m_c[2]; |
| |
| float s = clamp(floorf((d - low) * scale + .5f) * inv_n, 0.0f, 1.0f); |
| |
| float itr = sr + dir * s; |
| float itg = sg + dig * s; |
| float itb = sb + dib * s; |
| |
| float dr = itr - (float)pC->m_c[0]; |
| float dg = itg - (float)pC->m_c[1]; |
| float db = itb - (float)pC->m_c[2]; |
| |
| total_errf += dr * dr + dg * dg + db * db; |
| } |
| } |
| |
| total_err = (int64_t)total_errf; |
| |
| return total_err; |
| } |
| |
| static uint64_t color_cell_compression_est_mode7( uint32_t mode, const color_cell_compressor_params * pParams, uint64_t best_err_so_far, uint32_t num_pixels, const color_quad_i * pPixels) |
| { |
| NOTE_UNUSED(best_err_so_far); |
| NOTE_UNUSED(mode); // only referenced by the assert below (compiled out under NDEBUG) |
| assert((mode == 7) && (pParams->m_num_selector_weights == 4)); |
| |
| float lr = 255, lg = 255, lb = 255, la = 255; |
| float hr = 0, hg = 0, hb = 0, ha = 0; |
| for ( uint32_t i = 0; i < num_pixels; i++) |
| { |
| const color_quad_i * pC = &pPixels[i]; |
| |
| float r = (float)(pC->m_c[0]); |
| float g = (float)(pC->m_c[1]); |
| float b = (float)(pC->m_c[2]); |
| float a = (float)(pC->m_c[3]); |
| |
| lr = min(lr, r); |
| lg = min(lg, g); |
| lb = min(lb, b); |
| la = min(la, a); |
| |
| hr = max(hr, r); |
| hg = max(hg, g); |
| hb = max(hb, b); |
| ha = max(ha, a); |
| } |
| |
| const uint32_t N = 4; |
| |
| uint64_t total_err = 0; |
| |
| float sr = lr; |
| float sg = lg; |
| float sb = lb; |
| float sa = la; |
| |
| float dir = hr - lr; |
| float dig = hg - lg; |
| float dib = hb - lb; |
| float dia = ha - la; |
| |
| float far = dir; |
| float fag = dig; |
| float fab = dib; |
| float faa = dia; |
| |
| float low = far * sr + fag * sg + fab * sb + faa * sa; |
| float high = far * hr + fag * hg + fab * hb + faa * ha; |
| |
| float scale = ((float)N - 1) / ((float)(high - low) + BC7E_DENOM_BIAS); |
| float inv_n = 1.0f / ((float)N - 1); |
| |
| float total_errf = 0; |
| |
| // We don't handle perceptual very well here, but the difference is very slight (<.05 dB avg Luma PSNR across a large corpus) and the perf lost was high (2x slower). |
| if ( (!pParams->m_perceptual) && ((pParams->m_weights[0] != 1) || (pParams->m_weights[1] != 1) || (pParams->m_weights[2] != 1) || (pParams->m_weights[3] != 1)) ) |
| { |
| float wr = (float)(pParams->m_weights[0]); |
| float wg = (float)(pParams->m_weights[1]); |
| float wb = (float)(pParams->m_weights[2]); |
| float wa = (float)(pParams->m_weights[3]); |
| |
| for ( uint32_t i = 0; i < num_pixels; i++) |
| { |
| const color_quad_i * pC = &pPixels[i]; |
| |
| float d = far * (float)pC->m_c[0] + fag * (float)pC->m_c[1] + fab * (float)pC->m_c[2] + faa * (float)pC->m_c[3]; |
| |
| float s = clamp(floorf((d - low) * scale + .5f) * inv_n, 0.0f, 1.0f); |
| |
| float itr = sr + dir * s; |
| float itg = sg + dig * s; |
| float itb = sb + dib * s; |
| float ita = sa + dia * s; |
| |
| float dr = itr - (float)pC->m_c[0]; |
| float dg = itg - (float)pC->m_c[1]; |
| float db = itb - (float)pC->m_c[2]; |
| float da = ita - (float)pC->m_c[3]; |
| |
| total_errf += wr * dr * dr + wg * dg * dg + wb * db * db + wa * da * da; |
| } |
| } |
| else |
| { |
| for ( uint32_t i = 0; i < num_pixels; i++) |
| { |
| const color_quad_i * pC = &pPixels[i]; |
| |
| float d = far * (float)pC->m_c[0] + fag * (float)pC->m_c[1] + fab * (float)pC->m_c[2] + faa * (float)pC->m_c[3]; |
| |
| float s = clamp(floorf((d - low) * scale + .5f) * inv_n, 0.0f, 1.0f); |
| |
| float itr = sr + dir * s; |
| float itg = sg + dig * s; |
| float itb = sb + dib * s; |
| float ita = sa + dia * s; |
| |
| float dr = itr - (float)pC->m_c[0]; |
| float dg = itg - (float)pC->m_c[1]; |
| float db = itb - (float)pC->m_c[2]; |
| float da = ita - (float)pC->m_c[3]; |
| |
| total_errf += dr * dr + dg * dg + db * db + da * da; |
| } |
| } |
| |
| total_err = (int64_t)total_errf; |
| |
| return total_err; |
| } |
| |
| static uint32_t estimate_partition( uint32_t mode, const color_quad_i * pPixels, const bc7e_compress_block_params * pComp_params) |
| { |
| const uint32_t total_subsets = g_bc7_num_subsets[mode]; |
| uint32_t total_partitions = minimumu(pComp_params->m_max_partitions_mode[mode], 1U << g_bc7_partition_bits[mode]); |
| |
| if (total_partitions <= 1) |
| return 0; |
| |
| uint64_t best_err = UINT64_MAX; |
| uint32_t best_partition = 0; |
| |
| color_cell_compressor_params params; |
| color_cell_compressor_params_clear(¶ms); |
| |
| params.m_pSelector_weights = (g_bc7_color_index_bitcount[mode] == 2) ? g_bc7_weights2 : g_bc7_weights3; |
| params.m_num_selector_weights = 1 << g_bc7_color_index_bitcount[mode]; |
| |
| memcpy(params.m_weights, pComp_params->m_weights, sizeof(params.m_weights)); |
| |
| if (mode >= 6) |
| { |
| params.m_weights[0] *= pComp_params->m_alpha_settings.m_mode67_error_weight_mul[0]; |
| params.m_weights[1] *= pComp_params->m_alpha_settings.m_mode67_error_weight_mul[1]; |
| params.m_weights[2] *= pComp_params->m_alpha_settings.m_mode67_error_weight_mul[2]; |
| params.m_weights[3] *= pComp_params->m_alpha_settings.m_mode67_error_weight_mul[3]; |
| } |
| |
| params.m_perceptual = pComp_params->m_perceptual; |
| |
| for ( uint32_t partition = 0; partition < total_partitions; partition++) |
| { |
| const int * pPartition = (total_subsets == 3) ? &g_bc7_partition3[partition * 16] : &g_bc7_partition2[partition * 16]; |
| |
| color_quad_i subset_colors[3][16]; |
| uint32_t subset_total_colors[3]; |
| subset_total_colors[0] = 0; |
| subset_total_colors[1] = 0; |
| subset_total_colors[2] = 0; |
| |
| for ( uint32_t index = 0; index < 16; index++) |
| { |
| const uint32_t p = pPartition[index]; |
| |
| subset_colors[p][subset_total_colors[p]] = pPixels[index]; |
| subset_total_colors[p]++; |
| } |
| |
| uint64_t total_subset_err = 0; |
| |
| for ( uint32_t subset = 0; subset < total_subsets; subset++) |
| { |
| uint64_t err; |
| if (mode == 7) |
| err = color_cell_compression_est_mode7(mode, ¶ms, best_err, subset_total_colors[subset], &subset_colors[subset][0]); |
| else |
| err = color_cell_compression_est(mode, ¶ms, best_err, subset_total_colors[subset], &subset_colors[subset][0]); |
| |
| total_subset_err += err; |
| |
| } // subset |
| |
| if (total_subset_err < best_err) |
| { |
| best_err = total_subset_err; |
| best_partition = partition; |
| if (!best_err) |
| break; |
| } |
| |
| if (total_subsets == 2) |
| { |
| if ((partition == BC7E_2SUBSET_CHECKERBOARD_PARTITION_INDEX) && (best_partition != BC7E_2SUBSET_CHECKERBOARD_PARTITION_INDEX)) |
| break; |
| } |
| |
| } // partition |
| |
| return best_partition; |
| } |
| |
| struct solution |
| { |
| uint32_t m_index; |
| uint64_t m_err; |
| }; |
| |
| static uint32_t estimate_partition_list( uint32_t mode, const color_quad_i * pPixels, const bc7e_compress_block_params * pComp_params, |
| solution * pSolutions, int32_t max_solutions) |
| { |
| const int32_t orig_max_solutions = max_solutions; |
| |
| const uint32_t total_subsets = g_bc7_num_subsets[mode]; |
| uint32_t total_partitions = minimumu(pComp_params->m_max_partitions_mode[mode], 1U << g_bc7_partition_bits[mode]); |
| |
| if (total_partitions <= 1) |
| { |
| pSolutions[0].m_index = 0; |
| pSolutions[0].m_err = 0; |
| return 1; |
| } |
| else if (max_solutions >= (int)total_partitions) |
| { |
| for ( int i = 0; i < (int)total_partitions; i++) |
| { |
| pSolutions[i].m_index = i; |
| pSolutions[i].m_err = i; |
| } |
| return total_partitions; |
| } |
| |
| const int32_t HIGH_FREQUENCY_SORTED_PARTITION_THRESHOLD = 4; |
| if (total_subsets == 2) |
| { |
| if (max_solutions < HIGH_FREQUENCY_SORTED_PARTITION_THRESHOLD) |
| max_solutions = HIGH_FREQUENCY_SORTED_PARTITION_THRESHOLD; |
| } |
| |
| color_cell_compressor_params params; |
| color_cell_compressor_params_clear(¶ms); |
| |
| params.m_pSelector_weights = (g_bc7_color_index_bitcount[mode] == 2) ? g_bc7_weights2 : g_bc7_weights3; |
| params.m_num_selector_weights = 1 << g_bc7_color_index_bitcount[mode]; |
| |
| memcpy(params.m_weights, pComp_params->m_weights, sizeof(params.m_weights)); |
| |
| if (mode >= 6) |
| { |
| params.m_weights[0] *= pComp_params->m_alpha_settings.m_mode67_error_weight_mul[0]; |
| params.m_weights[1] *= pComp_params->m_alpha_settings.m_mode67_error_weight_mul[1]; |
| params.m_weights[2] *= pComp_params->m_alpha_settings.m_mode67_error_weight_mul[2]; |
| params.m_weights[3] *= pComp_params->m_alpha_settings.m_mode67_error_weight_mul[3]; |
| } |
| |
| params.m_perceptual = pComp_params->m_perceptual; |
| |
| int32_t num_solutions = 0; |
| |
| for ( uint32_t partition = 0; partition < total_partitions; partition++) |
| { |
| const int * pPartition = (total_subsets == 3) ? &g_bc7_partition3[partition * 16] : &g_bc7_partition2[partition * 16]; |
| |
| color_quad_i subset_colors[3][16]; |
| uint32_t subset_total_colors[3]; |
| subset_total_colors[0] = 0; |
| subset_total_colors[1] = 0; |
| subset_total_colors[2] = 0; |
| |
| for ( uint32_t index = 0; index < 16; index++) |
| { |
| const uint32_t p = pPartition[index]; |
| |
| subset_colors[p][subset_total_colors[p]] = pPixels[index]; |
| subset_total_colors[p]++; |
| } |
| |
| uint64_t total_subset_err = 0; |
| |
| for ( uint32_t subset = 0; subset < total_subsets; subset++) |
| { |
| uint64_t err; |
| if (mode == 7) |
| err = color_cell_compression_est_mode7(mode, ¶ms, UINT64_MAX, subset_total_colors[subset], &subset_colors[subset][0]); |
| else |
| err = color_cell_compression_est(mode, ¶ms, UINT64_MAX, subset_total_colors[subset], &subset_colors[subset][0]); |
| |
| total_subset_err += err; |
| |
| } // subset |
| |
| int32_t i; |
| for (i = 0; i < num_solutions; i++) |
| { |
| if (total_subset_err < pSolutions[i].m_err) |
| break; |
| } |
| |
| if (i < num_solutions) |
| { |
| int32_t solutions_to_move = (max_solutions - 1) - i; |
| int32_t num_elements_at_i = num_solutions - i; |
| if (solutions_to_move > num_elements_at_i) |
| solutions_to_move = num_elements_at_i; |
| |
| assert(((i + 1) + solutions_to_move) <= max_solutions); |
| assert((i + solutions_to_move) <= num_solutions); |
| |
| for (int32_t j = solutions_to_move - 1; j >= 0; --j) |
| { |
| pSolutions[i + j + 1] = pSolutions[i + j]; |
| } |
| } |
| |
| if (num_solutions < max_solutions) |
| num_solutions++; |
| |
| if (i < num_solutions) |
| { |
| pSolutions[i].m_err = total_subset_err; |
| |
| pSolutions[i].m_index = partition; |
| } |
| |
| #if BC7E_NON_DETERMINISTIC |
| if ((total_subsets == 2) && (partition == BC7E_2SUBSET_CHECKERBOARD_PARTITION_INDEX)) |
| { |
| if (all(i >= HIGH_FREQUENCY_SORTED_PARTITION_THRESHOLD)) |
| break; |
| } |
| #endif |
| |
| } // partition |
| |
| #if 0 |
| for ( int i = 0; i < num_solutions; i++) |
| { |
| assert(pSolutions[i].m_index < total_partitions); |
| } |
| |
| for ( int i = 0; i < (num_solutions - 1); i++) |
| { |
| assert(pSolutions[i].m_err <= pSolutions[i + 1].m_err); |
| } |
| #endif |
| |
| return min(num_solutions, orig_max_solutions); |
| } |
| |
| static inline void set_block_bits(uint8_t *pBytes, uint32_t val, uint32_t num_bits, uint32_t * pCur_ofs) |
| { |
| assert(num_bits < 32); |
| uint32_t limit = 1U << num_bits; |
| assert(val < limit); |
| NOTE_UNUSED(limit); // only referenced by the assert above (compiled out under NDEBUG) |
| |
| while (num_bits) |
| { |
| const uint32_t n = minimumu(8 - (*pCur_ofs & 7), num_bits); |
| |
| pBytes[*pCur_ofs >> 3] |= (uint8_t)(val << (*pCur_ofs & 7)); |
| |
| val >>= n; |
| num_bits -= n; |
| *pCur_ofs += n; |
| } |
| |
| assert(*pCur_ofs <= 128); |
| } |
| |
| struct bc7_optimization_results |
| { |
| uint32_t m_mode; |
| uint32_t m_partition; |
| int m_selectors[16]; |
| int m_alpha_selectors[16]; |
| color_quad_i m_low[3]; |
| color_quad_i m_high[3]; |
| uint32_t m_pbits[3][2]; |
| uint32_t m_rotation; |
| uint32_t m_index_selector; |
| // True if the WINNING encoding used the one-color optimal-endpoint LUT on any |
| // subset (OR'd across the winning mode's subsets). Set at each winner-update. |
| bool m_used_lut; |
| }; |
| |
| static void encode_bc7_block(void *pBlock, const bc7_optimization_results * pResults) |
| { |
| const uint32_t best_mode = pResults->m_mode; |
| |
| const uint32_t total_subsets = g_bc7_num_subsets[best_mode]; |
| |
| const uint32_t total_partitions = 1 << g_bc7_partition_bits[best_mode]; |
| |
| const int *pPartition; |
| if (total_subsets == 1) |
| pPartition = &g_bc7_partition1[0]; |
| else if (total_subsets == 2) |
| pPartition = &g_bc7_partition2[pResults->m_partition * 16]; |
| else |
| pPartition = &g_bc7_partition3[pResults->m_partition * 16]; |
| |
| int color_selectors[16]; |
| for ( int i = 0; i < 16; i++) |
| color_selectors[i] = pResults->m_selectors[i]; |
| |
| int alpha_selectors[16]; |
| for ( int i = 0; i < 16; i++) |
| alpha_selectors[i] = pResults->m_alpha_selectors[i]; |
| |
| color_quad_i low[3], high[3]; |
| low[0] = pResults->m_low[0]; |
| low[1] = pResults->m_low[1]; |
| low[2] = pResults->m_low[2]; |
| |
| high[0] = pResults->m_high[0]; |
| high[1] = pResults->m_high[1]; |
| high[2] = pResults->m_high[2]; |
| |
| uint32_t pbits[3][2]; |
| for ( int i = 0; i < 3; i++) |
| { |
| pbits[i][0] = pResults->m_pbits[i][0]; |
| pbits[i][1] = pResults->m_pbits[i][1]; |
| } |
| |
| int anchor[3]; |
| anchor[0] = -1; |
| anchor[1] = -1; |
| anchor[2] = -1; |
| |
| for ( uint32_t k = 0; k < total_subsets; k++) |
| { |
| uint32_t anchor_index = 0; |
| if (k) |
| { |
| if ((total_subsets == 3) && (k == 1)) |
| { |
| anchor_index = g_bc7_table_anchor_index_third_subset_1[pResults->m_partition]; |
| } |
| else if ((total_subsets == 3) && (k == 2)) |
| { |
| anchor_index = g_bc7_table_anchor_index_third_subset_2[pResults->m_partition]; |
| } |
| else |
| { |
| anchor_index = g_bc7_table_anchor_index_second_subset[pResults->m_partition]; |
| } |
| } |
| |
| anchor[k] = anchor_index; |
| |
| const uint32_t color_index_bits = get_bc7_color_index_size(best_mode, pResults->m_index_selector); |
| const uint32_t num_color_indices = 1 << color_index_bits; |
| |
| if (color_selectors[anchor_index] & (num_color_indices >> 1)) |
| { |
| for ( uint32_t i = 0; i < 16; i++) |
| { |
| if ((uint32_t)pPartition[i] == k) |
| color_selectors[i] = (num_color_indices - 1) - color_selectors[i]; |
| } |
| |
| if (get_bc7_mode_has_seperate_alpha_selectors(best_mode)) |
| { |
| for ( uint32_t q = 0; q < 3; q++) |
| { |
| int t = low[k].m_c[q]; |
| low[k].m_c[q] = high[k].m_c[q]; |
| high[k].m_c[q] = t; |
| } |
| } |
| else |
| { |
| color_quad_i tmp = low[k]; |
| low[k] = high[k]; |
| high[k] = tmp; |
| } |
| |
| if (!g_bc7_mode_has_shared_p_bits[best_mode]) |
| { |
| uint32_t t = pbits[k][0]; |
| pbits[k][0] = pbits[k][1]; |
| pbits[k][1] = t; |
| } |
| } |
| |
| if (get_bc7_mode_has_seperate_alpha_selectors(best_mode)) |
| { |
| const uint32_t alpha_index_bits = get_bc7_alpha_index_size(best_mode, pResults->m_index_selector); |
| const uint32_t num_alpha_indices = 1 << alpha_index_bits; |
| |
| if (alpha_selectors[anchor_index] & (num_alpha_indices >> 1)) |
| { |
| for ( uint32_t i = 0; i < 16; i++) |
| { |
| if ((uint32_t)pPartition[i] == k) |
| alpha_selectors[i] = (num_alpha_indices - 1) - alpha_selectors[i]; |
| } |
| |
| int t = low[k].m_c[3]; |
| low[k].m_c[3] = high[k].m_c[3]; |
| high[k].m_c[3] = t; |
| } |
| } |
| } |
| |
| uint8_t *pBlock_bytes = (uint8_t *)(pBlock); |
| memset(pBlock_bytes, 0, BC7E_BLOCK_SIZE); |
| |
| uint32_t cur_bit_ofs = 0; |
| |
| set_block_bits(pBlock_bytes, 1 << best_mode, best_mode + 1, &cur_bit_ofs); |
| |
| if ((best_mode == 4) || (best_mode == 5)) |
| set_block_bits(pBlock_bytes, pResults->m_rotation, 2, &cur_bit_ofs); |
| |
| if (best_mode == 4) |
| set_block_bits(pBlock_bytes, pResults->m_index_selector, 1, &cur_bit_ofs); |
| |
| if (total_partitions > 1) |
| set_block_bits(pBlock_bytes, pResults->m_partition, (total_partitions == 64) ? 6 : 4, &cur_bit_ofs); |
| |
| const uint32_t total_comps = (best_mode >= 4) ? 4 : 3; |
| for ( uint32_t comp = 0; comp < total_comps; comp++) |
| { |
| for ( uint32_t subset = 0; subset < total_subsets; subset++) |
| { |
| set_block_bits(pBlock_bytes, low[subset].m_c[comp], (comp == 3) ? g_bc7_alpha_precision_table[best_mode] : g_bc7_color_precision_table[best_mode], &cur_bit_ofs); |
| set_block_bits(pBlock_bytes, high[subset].m_c[comp], (comp == 3) ? g_bc7_alpha_precision_table[best_mode] : g_bc7_color_precision_table[best_mode], &cur_bit_ofs); |
| } |
| } |
| |
| if (g_bc7_mode_has_p_bits[best_mode]) |
| { |
| for ( uint32_t subset = 0; subset < total_subsets; subset++) |
| { |
| set_block_bits(pBlock_bytes, pbits[subset][0], 1, &cur_bit_ofs); |
| if (!g_bc7_mode_has_shared_p_bits[best_mode]) |
| set_block_bits(pBlock_bytes, pbits[subset][1], 1, &cur_bit_ofs); |
| } |
| } |
| |
| for ( uint32_t y = 0; y < 4; y++) |
| { |
| for ( uint32_t x = 0; x < 4; x++) |
| { |
| int idx = x + y * 4; |
| |
| uint32_t n = pResults->m_index_selector ? get_bc7_alpha_index_size(best_mode, pResults->m_index_selector) : get_bc7_color_index_size(best_mode, pResults->m_index_selector); |
| |
| if ((idx == anchor[0]) || (idx == anchor[1]) || (idx == anchor[2])) |
| n--; |
| |
| set_block_bits(pBlock_bytes, pResults->m_index_selector ? alpha_selectors[idx] : color_selectors[idx], n, &cur_bit_ofs); |
| } |
| } |
| |
| if (get_bc7_mode_has_seperate_alpha_selectors(best_mode)) |
| { |
| for ( uint32_t y = 0; y < 4; y++) |
| { |
| for ( uint32_t x = 0; x < 4; x++) |
| { |
| int idx = x + y * 4; |
| |
| uint32_t n = pResults->m_index_selector ? get_bc7_color_index_size(best_mode, pResults->m_index_selector) : get_bc7_alpha_index_size(best_mode, pResults->m_index_selector); |
| |
| if ((idx == anchor[0]) || (idx == anchor[1]) || (idx == anchor[2])) |
| n--; |
| |
| set_block_bits(pBlock_bytes, pResults->m_index_selector ? color_selectors[idx] : alpha_selectors[idx], n, &cur_bit_ofs); |
| } |
| } |
| } |
| |
| assert(cur_bit_ofs == 128); |
| } |
| |
| static inline void encode_bc7_block_mode6(void *pBlock, bc7_optimization_results * pResults) |
| { |
| color_quad_i low, high; |
| uint32_t pbits[2]; |
| |
| uint32_t invert_selectors = 0; |
| if (pResults->m_selectors[0] & 8) |
| { |
| invert_selectors = 15; |
| |
| low = pResults->m_high[0]; |
| high = pResults->m_low[0]; |
| |
| pbits[0] = pResults->m_pbits[0][1]; |
| pbits[1] = pResults->m_pbits[0][0]; |
| } |
| else |
| { |
| low = pResults->m_low[0]; |
| high = pResults->m_high[0]; |
| |
| pbits[0] = pResults->m_pbits[0][0]; |
| pbits[1] = pResults->m_pbits[0][1]; |
| } |
| |
| uint64_t l = 0, h = 0; |
| |
| l = 1 << 6; |
| |
| l |= (low.m_c[0] << 7); |
| l |= (high.m_c[0] << 14); |
| |
| l |= (low.m_c[1] << 21); |
| l |= ((uint64_t)high.m_c[1] << 28); |
| |
| l |= ((uint64_t)low.m_c[2] << 35); |
| l |= ((uint64_t)high.m_c[2] << 42); |
| |
| l |= ((uint64_t)low.m_c[3] << 49); |
| l |= ((uint64_t)high.m_c[3] << 56); |
| |
| l |= ((uint64_t)pbits[0] << 63); |
| |
| h = pbits[1]; |
| |
| h |= ((invert_selectors ^ pResults->m_selectors[0]) << 1); |
| |
| // TODO: Just invert all these bits in one single operation, not as individual |
| h |= ((invert_selectors ^ pResults->m_selectors[1]) << 4); |
| h |= ((invert_selectors ^ pResults->m_selectors[2]) << 8); |
| h |= ((invert_selectors ^ pResults->m_selectors[3]) << 12); |
| h |= ((invert_selectors ^ pResults->m_selectors[4]) << 16); |
| |
| h |= ((invert_selectors ^ pResults->m_selectors[5]) << 20); |
| h |= ((invert_selectors ^ pResults->m_selectors[6]) << 24); |
| h |= ((invert_selectors ^ pResults->m_selectors[7]) << 28); |
| h |= ((uint64_t)(invert_selectors ^ pResults->m_selectors[8]) << 32); |
| |
| h |= ((uint64_t)(invert_selectors ^ pResults->m_selectors[9]) << 36); |
| h |= ((uint64_t)(invert_selectors ^ pResults->m_selectors[10]) << 40); |
| h |= ((uint64_t)(invert_selectors ^ pResults->m_selectors[11]) << 44); |
| h |= ((uint64_t)(invert_selectors ^ pResults->m_selectors[12]) << 48); |
| |
| h |= ((uint64_t)(invert_selectors ^ pResults->m_selectors[13]) << 52); |
| h |= ((uint64_t)(invert_selectors ^ pResults->m_selectors[14]) << 56); |
| h |= ((uint64_t)(invert_selectors ^ pResults->m_selectors[15]) << 60); |
| |
| ((uint64_t *)(pBlock))[0] = l; |
| |
| ((uint64_t *)(pBlock))[1] = h; |
| } |
| |
| static void handle_alpha_block_mode4(const color_quad_i * pPixels, const bc7e_compress_block_params * pComp_params, color_cell_compressor_params * pParams, uint32_t lo_a, uint32_t hi_a, |
| bc7_optimization_results * pOpt_results4, uint64_t * pMode4_err) |
| { |
| pParams->m_has_alpha = false; |
| pParams->m_comp_bits = 5; |
| pParams->m_has_pbits = false; |
| pParams->m_endpoints_share_pbit = false; |
| pParams->m_perceptual = pComp_params->m_perceptual; |
| |
| for ( uint32_t index_selector = 0; index_selector < 2; index_selector++) |
| { |
| if ((pComp_params->m_mode4_index_mask & (1 << index_selector)) == 0) |
| continue; |
| |
| if (index_selector) |
| { |
| pParams->m_pSelector_weights = g_bc7_weights3; |
| pParams->m_pSelector_weightsx = (const vec4F * )&g_bc7_weights3x[0]; |
| pParams->m_num_selector_weights = 8; |
| } |
| else |
| { |
| pParams->m_pSelector_weights = g_bc7_weights2; |
| pParams->m_pSelector_weightsx = (const vec4F * )&g_bc7_weights2x[0]; |
| pParams->m_num_selector_weights = 4; |
| } |
| |
| color_cell_compressor_results results; |
| |
| int selectors[16]; |
| results.m_pSelectors = selectors; |
| |
| int selectors_temp[16]; |
| results.m_pSelectors_temp = selectors_temp; |
| |
| uint64_t trial_err = color_cell_compression(4, pParams, &results, pComp_params, 16, pPixels, true); |
| assert(trial_err == results.m_best_overall_err); |
| |
| uint32_t la = minimumi((lo_a + 2) >> 2, 63); |
| uint32_t ha = minimumi((hi_a + 2) >> 2, 63); |
| |
| if (la == ha) |
| { |
| if (lo_a != hi_a) |
| { |
| if (ha != 63) |
| ha++; |
| else if (la != 0) |
| la--; |
| } |
| } |
| |
| uint64_t best_alpha_err = UINT64_MAX; |
| uint32_t best_la = 0, best_ha = 0; |
| int best_alpha_selectors[16] = {}; |
| |
| for ( int32_t pass = 0; pass < 2; pass++) |
| { |
| int32_t vals[8]; |
| |
| if (index_selector == 0) |
| { |
| vals[0] = (la << 2) | (la >> 4); |
| vals[7] = (ha << 2) | (ha >> 4); |
| |
| for ( uint32_t i = 1; i < 7; i++) |
| vals[i] = (vals[0] * (64 - g_bc7_weights3[i]) + vals[7] * g_bc7_weights3[i] + 32) >> 6; |
| } |
| else |
| { |
| vals[0] = (la << 2) | (la >> 4); |
| vals[3] = (ha << 2) | (ha >> 4); |
| |
| const int32_t w_s1 = 21, w_s2 = 43; |
| vals[1] = (vals[0] * (64 - w_s1) + vals[3] * w_s1 + 32) >> 6; |
| vals[2] = (vals[0] * (64 - w_s2) + vals[3] * w_s2 + 32) >> 6; |
| } |
| |
| uint64_t trial_alpha_err = 0; |
| |
| int trial_alpha_selectors[16]; |
| for ( uint32_t i = 0; i < 16; i++) |
| { |
| const int32_t a = pPixels[i].m_c[3]; |
| |
| int s = 0; |
| int32_t be = iabs32(a - vals[0]); |
| |
| int e = iabs32(a - vals[1]); if (e < be) { be = e; s = 1; } |
| e = iabs32(a - vals[2]); if (e < be) { be = e; s = 2; } |
| e = iabs32(a - vals[3]); if (e < be) { be = e; s = 3; } |
| |
| if (index_selector == 0) |
| { |
| e = iabs32(a - vals[4]); if (e < be) { be = e; s = 4; } |
| e = iabs32(a - vals[5]); if (e < be) { be = e; s = 5; } |
| e = iabs32(a - vals[6]); if (e < be) { be = e; s = 6; } |
| e = iabs32(a - vals[7]); if (e < be) { be = e; s = 7; } |
| } |
| |
| trial_alpha_err += (be * be) * pParams->m_weights[3]; |
| |
| trial_alpha_selectors[i] = s; |
| } |
| |
| if (trial_alpha_err < best_alpha_err) |
| { |
| best_alpha_err = trial_alpha_err; |
| best_la = la; |
| best_ha = ha; |
| for ( uint32_t i = 0; i < 16; i++) |
| best_alpha_selectors[i] = trial_alpha_selectors[i]; |
| } |
| |
| if (pass == 0) |
| { |
| float xl, xh; |
| compute_least_squares_endpoints_a(16, trial_alpha_selectors, index_selector ? (const vec4F * )&g_bc7_weights2x[0] : (const vec4F * )&g_bc7_weights3x[0], &xl, &xh, pPixels); |
| if (xl > xh) |
| swapf(&xl, &xh); |
| la = clampi((int)floor(xl * (63.0f / 255.0f) + .5f), 0, 63); |
| ha = clampi((int)floor(xh * (63.0f / 255.0f) + .5f), 0, 63); |
| } |
| |
| } // pass |
| |
| if (pComp_params->m_uber_level > 0) |
| { |
| const int D = min((int)pComp_params->m_uber_level, 3); |
| for ( int ld = -D; ld <= D; ld++) |
| { |
| for ( int hd = -D; hd <= D; hd++) |
| { |
| la = clamp((int)best_la + ld, 0, 63); |
| ha = clamp((int)best_ha + hd, 0, 63); |
| |
| int32_t vals[8]; |
| |
| if (index_selector == 0) |
| { |
| vals[0] = (la << 2) | (la >> 4); |
| vals[7] = (ha << 2) | (ha >> 4); |
| |
| for ( uint32_t i = 1; i < 7; i++) |
| vals[i] = (vals[0] * (64 - g_bc7_weights3[i]) + vals[7] * g_bc7_weights3[i] + 32) >> 6; |
| } |
| else |
| { |
| vals[0] = (la << 2) | (la >> 4); |
| vals[3] = (ha << 2) | (ha >> 4); |
| |
| const int32_t w_s1 = 21, w_s2 = 43; |
| vals[1] = (vals[0] * (64 - w_s1) + vals[3] * w_s1 + 32) >> 6; |
| vals[2] = (vals[0] * (64 - w_s2) + vals[3] * w_s2 + 32) >> 6; |
| } |
| |
| uint64_t trial_alpha_err = 0; |
| |
| int trial_alpha_selectors[16]; |
| for ( uint32_t i = 0; i < 16; i++) |
| { |
| const int32_t a = pPixels[i].m_c[3]; |
| |
| int s = 0; |
| int32_t be = iabs32(a - vals[0]); |
| |
| int e = iabs32(a - vals[1]); if (e < be) { be = e; s = 1; } |
| e = iabs32(a - vals[2]); if (e < be) { be = e; s = 2; } |
| e = iabs32(a - vals[3]); if (e < be) { be = e; s = 3; } |
| |
| if (index_selector == 0) |
| { |
| e = iabs32(a - vals[4]); if (e < be) { be = e; s = 4; } |
| e = iabs32(a - vals[5]); if (e < be) { be = e; s = 5; } |
| e = iabs32(a - vals[6]); if (e < be) { be = e; s = 6; } |
| e = iabs32(a - vals[7]); if (e < be) { be = e; s = 7; } |
| } |
| |
| trial_alpha_err += (be * be) * pParams->m_weights[3]; |
| |
| trial_alpha_selectors[i] = s; |
| } |
| |
| if (trial_alpha_err < best_alpha_err) |
| { |
| best_alpha_err = trial_alpha_err; |
| best_la = la; |
| best_ha = ha; |
| for ( uint32_t i = 0; i < 16; i++) |
| best_alpha_selectors[i] = trial_alpha_selectors[i]; |
| } |
| |
| } // hd |
| |
| } // ld |
| } |
| |
| trial_err += best_alpha_err; |
| |
| if (trial_err < *pMode4_err) |
| { |
| *pMode4_err = trial_err; |
| |
| pOpt_results4->m_mode = 4; |
| pOpt_results4->m_index_selector = index_selector; |
| pOpt_results4->m_rotation = 0; |
| pOpt_results4->m_partition = 0; |
| pOpt_results4->m_used_lut = results.m_used_lut; |
| |
| pOpt_results4->m_low[0] = results.m_low_endpoint; |
| pOpt_results4->m_high[0] = results.m_high_endpoint; |
| pOpt_results4->m_low[0].m_c[3] = best_la; |
| pOpt_results4->m_high[0].m_c[3] = best_ha; |
| |
| for ( uint32_t i = 0; i < 16; i++) |
| pOpt_results4->m_selectors[i] = selectors[i]; |
| |
| for ( uint32_t i = 0; i < 16; i++) |
| pOpt_results4->m_alpha_selectors[i] = best_alpha_selectors[i]; |
| } |
| |
| } // index_selector |
| } |
| |
| static void handle_alpha_block_mode5(const color_quad_i * pPixels, const bc7e_compress_block_params * pComp_params, color_cell_compressor_params * pParams, uint32_t lo_a, uint32_t hi_a, |
| bc7_optimization_results * pOpt_results5, uint64_t * pMode5_err) |
| { |
| pParams->m_pSelector_weights = g_bc7_weights2; |
| pParams->m_pSelector_weightsx = (const vec4F * )&g_bc7_weights2x[0]; |
| pParams->m_num_selector_weights = 4; |
| |
| pParams->m_comp_bits = 7; |
| pParams->m_has_alpha = false; |
| pParams->m_has_pbits = false; |
| pParams->m_endpoints_share_pbit = false; |
| |
| pParams->m_perceptual = pComp_params->m_perceptual; |
| |
| color_cell_compressor_results results5; |
| results5.m_pSelectors = pOpt_results5->m_selectors; |
| |
| int selectors_temp[16]; |
| results5.m_pSelectors_temp = selectors_temp; |
| |
| *pMode5_err = color_cell_compression(5, pParams, &results5, pComp_params, 16, pPixels, true); |
| assert(*pMode5_err == results5.m_best_overall_err); |
| |
| pOpt_results5->m_low[0] = results5.m_low_endpoint; |
| pOpt_results5->m_high[0] = results5.m_high_endpoint; |
| |
| cif (lo_a == hi_a) |
| { |
| pOpt_results5->m_low[0].m_c[3] = lo_a; |
| pOpt_results5->m_high[0].m_c[3] = hi_a; |
| for ( uint32_t i = 0; i < 16; i++) |
| pOpt_results5->m_alpha_selectors[i] = 0; |
| } |
| else |
| { |
| uint64_t mode5_alpha_err = UINT64_MAX; |
| |
| for ( uint32_t pass = 0; pass < 2; pass++) |
| { |
| int32_t vals[4]; |
| vals[0] = lo_a; |
| vals[3] = hi_a; |
| |
| const int32_t w_s1 = 21, w_s2 = 43; |
| vals[1] = (vals[0] * (64 - w_s1) + vals[3] * w_s1 + 32) >> 6; |
| vals[2] = (vals[0] * (64 - w_s2) + vals[3] * w_s2 + 32) >> 6; |
| |
| int trial_alpha_selectors[16]; |
| |
| uint64_t trial_alpha_err = 0; |
| for ( uint32_t i = 0; i < 16; i++) |
| { |
| const int32_t a = pPixels[i].m_c[3]; |
| |
| int s = 0; |
| int32_t be = iabs32(a - vals[0]); |
| int e = iabs32(a - vals[1]); if (e < be) { be = e; s = 1; } |
| e = iabs32(a - vals[2]); if (e < be) { be = e; s = 2; } |
| e = iabs32(a - vals[3]); if (e < be) { be = e; s = 3; } |
| |
| trial_alpha_selectors[i] = s; |
| |
| trial_alpha_err += (be * be) * pParams->m_weights[3]; |
| } |
| |
| if (trial_alpha_err < mode5_alpha_err) |
| { |
| mode5_alpha_err = trial_alpha_err; |
| pOpt_results5->m_low[0].m_c[3] = lo_a; |
| pOpt_results5->m_high[0].m_c[3] = hi_a; |
| for ( uint32_t i = 0; i < 16; i++) |
| pOpt_results5->m_alpha_selectors[i] = trial_alpha_selectors[i]; |
| } |
| |
| if (!pass) |
| { |
| float xl, xh; |
| compute_least_squares_endpoints_a(16, trial_alpha_selectors, (const vec4F * )&g_bc7_weights2x[0], &xl, &xh, pPixels); |
| |
| uint32_t new_lo_a = clampi((int)floor(xl + .5f), 0, 255); |
| uint32_t new_hi_a = clampi((int)floor(xh + .5f), 0, 255); |
| if (new_lo_a > new_hi_a) |
| swapu(&new_lo_a, &new_hi_a); |
| |
| if ((new_lo_a == lo_a) && (new_hi_a == hi_a)) |
| break; |
| |
| lo_a = new_lo_a; |
| hi_a = new_hi_a; |
| } |
| } |
| |
| if (pComp_params->m_uber_level > 0) |
| { |
| const int D = min((int)pComp_params->m_uber_level, 3); |
| for ( int ld = -D; ld <= D; ld++) |
| { |
| for ( int hd = -D; hd <= D; hd++) |
| { |
| lo_a = clamp((int)pOpt_results5->m_low[0].m_c[3] + ld, 0, 255); |
| hi_a = clamp((int)pOpt_results5->m_high[0].m_c[3] + hd, 0, 255); |
| |
| int32_t vals[4]; |
| vals[0] = lo_a; |
| vals[3] = hi_a; |
| |
| const int32_t w_s1 = 21, w_s2 = 43; |
| vals[1] = (vals[0] * (64 - w_s1) + vals[3] * w_s1 + 32) >> 6; |
| vals[2] = (vals[0] * (64 - w_s2) + vals[3] * w_s2 + 32) >> 6; |
| |
| int trial_alpha_selectors[16]; |
| |
| uint64_t trial_alpha_err = 0; |
| for ( uint32_t i = 0; i < 16; i++) |
| { |
| const int32_t a = pPixels[i].m_c[3]; |
| |
| int s = 0; |
| int32_t be = iabs32(a - vals[0]); |
| int e = iabs32(a - vals[1]); if (e < be) { be = e; s = 1; } |
| e = iabs32(a - vals[2]); if (e < be) { be = e; s = 2; } |
| e = iabs32(a - vals[3]); if (e < be) { be = e; s = 3; } |
| |
| trial_alpha_selectors[i] = s; |
| |
| trial_alpha_err += (be * be) * pParams->m_weights[3]; |
| } |
| |
| if (trial_alpha_err < mode5_alpha_err) |
| { |
| mode5_alpha_err = trial_alpha_err; |
| pOpt_results5->m_low[0].m_c[3] = lo_a; |
| pOpt_results5->m_high[0].m_c[3] = hi_a; |
| for ( uint32_t i = 0; i < 16; i++) |
| pOpt_results5->m_alpha_selectors[i] = trial_alpha_selectors[i]; |
| } |
| |
| } // hd |
| |
| } // ld |
| } |
| |
| *pMode5_err += mode5_alpha_err; |
| } |
| |
| pOpt_results5->m_mode = 5; |
| pOpt_results5->m_index_selector = 0; |
| pOpt_results5->m_rotation = 0; |
| pOpt_results5->m_partition = 0; |
| pOpt_results5->m_used_lut = results5.m_used_lut; |
| } |
| |
| static void handle_alpha_block(void * pBlock, const color_quad_i * pPixels, const bc7e_compress_block_params * pComp_params, color_cell_compressor_params * pParams, uint32_t lo_a, uint32_t hi_a, int forced_partition = -1, uint64_t* pBest_err = nullptr, bool* pUsed_lut = nullptr) |
| { |
| pParams->m_perceptual = pComp_params->m_perceptual; |
| |
| bc7_optimization_results opt_results = {}; |
| |
| uint64_t best_err = UINT64_MAX; |
| |
| // Mode 4 |
| if (pComp_params->m_alpha_settings.m_use_mode4) |
| { |
| color_cell_compressor_params params4 = *pParams; |
| |
| const int num_rotations = (pComp_params->m_perceptual || (!pComp_params->m_alpha_settings.m_use_mode4_rotation)) ? 1 : 4; |
| for ( uint32_t rotation = 0; rotation < (uint32_t)num_rotations; rotation++) |
| { |
| if ((pComp_params->m_mode4_rotation_mask & (1 << rotation)) == 0) |
| continue; |
| |
| memcpy(params4.m_weights, pParams->m_weights, sizeof(params4.m_weights)); |
| if (rotation) |
| swapu(¶ms4.m_weights[rotation - 1], ¶ms4.m_weights[3]); |
| |
| color_quad_i rot_pixels[16]; |
| const color_quad_i * pTrial_pixels = pPixels; |
| uint32_t trial_lo_a = lo_a, trial_hi_a = hi_a; |
| if (rotation) |
| { |
| trial_lo_a = 255; |
| trial_hi_a = 0; |
| |
| for ( uint32_t i = 0; i < 16; i++) |
| { |
| color_quad_i c = pPixels[i]; |
| swapi(&c.m_c[3], &c.m_c[rotation - 1]); |
| rot_pixels[i] = c; |
| |
| trial_lo_a = minimumu(trial_lo_a, c.m_c[3]); |
| trial_hi_a = maximumu(trial_hi_a, c.m_c[3]); |
| } |
| |
| pTrial_pixels = rot_pixels; |
| } |
| |
| bc7_optimization_results trial_opt_results4; |
| |
| uint64_t trial_mode4_err = best_err; |
| |
| handle_alpha_block_mode4(pTrial_pixels, pComp_params, ¶ms4, trial_lo_a, trial_hi_a, &trial_opt_results4, &trial_mode4_err); |
| |
| if (trial_mode4_err < best_err) |
| { |
| best_err = trial_mode4_err; |
| |
| opt_results.m_mode = 4; |
| opt_results.m_index_selector = trial_opt_results4.m_index_selector; |
| opt_results.m_rotation = rotation; |
| opt_results.m_partition = 0; |
| opt_results.m_used_lut = trial_opt_results4.m_used_lut; |
| |
| opt_results.m_low[0] = trial_opt_results4.m_low[0]; |
| opt_results.m_high[0] = trial_opt_results4.m_high[0]; |
| |
| for ( uint32_t i = 0; i < 16; i++) |
| opt_results.m_selectors[i] = trial_opt_results4.m_selectors[i]; |
| |
| for ( uint32_t i = 0; i < 16; i++) |
| opt_results.m_alpha_selectors[i] = trial_opt_results4.m_alpha_selectors[i]; |
| } |
| } // rotation |
| } |
| |
| // Mode 6 |
| if (pComp_params->m_alpha_settings.m_use_mode6) |
| { |
| color_cell_compressor_params params6 = *pParams; |
| |
| params6.m_weights[0] *= pComp_params->m_alpha_settings.m_mode67_error_weight_mul[0]; |
| params6.m_weights[1] *= pComp_params->m_alpha_settings.m_mode67_error_weight_mul[1]; |
| params6.m_weights[2] *= pComp_params->m_alpha_settings.m_mode67_error_weight_mul[2]; |
| params6.m_weights[3] *= pComp_params->m_alpha_settings.m_mode67_error_weight_mul[3]; |
| |
| color_cell_compressor_results results6; |
| |
| params6.m_pSelector_weights = g_bc7_weights4; |
| params6.m_pSelector_weightsx = (const vec4F *)&g_bc7_weights4x[0]; |
| params6.m_num_selector_weights = 16; |
| |
| params6.m_comp_bits = 7; |
| params6.m_has_pbits = true; |
| params6.m_endpoints_share_pbit = false; |
| params6.m_has_alpha = true; |
| |
| int selectors[16]; |
| results6.m_pSelectors = selectors; |
| |
| int selectors_temp[16]; |
| results6.m_pSelectors_temp = selectors_temp; |
| |
| uint64_t mode6_err = color_cell_compression(6, ¶ms6, &results6, pComp_params, 16, pPixels, true); |
| assert(mode6_err == results6.m_best_overall_err); |
| |
| if (mode6_err < best_err) |
| { |
| best_err = mode6_err; |
| |
| opt_results.m_mode = 6; |
| opt_results.m_index_selector = 0; |
| opt_results.m_rotation = 0; |
| opt_results.m_partition = 0; |
| |
| opt_results.m_low[0] = results6.m_low_endpoint; |
| opt_results.m_high[0] = results6.m_high_endpoint; |
| |
| opt_results.m_pbits[0][0] = results6.m_pbits[0]; |
| opt_results.m_pbits[0][1] = results6.m_pbits[1]; |
| opt_results.m_used_lut = results6.m_used_lut; |
| |
| for ( int i = 0; i < 16; i++) |
| opt_results.m_selectors[i] = selectors[i]; |
| } |
| } |
| |
| // Mode 5 |
| if (pComp_params->m_alpha_settings.m_use_mode5) |
| { |
| color_cell_compressor_params params5 = *pParams; |
| |
| const int num_rotations = (pComp_params->m_perceptual || (!pComp_params->m_alpha_settings.m_use_mode5_rotation)) ? 1 : 4; |
| for ( uint32_t rotation = 0; rotation < (uint32_t)num_rotations; rotation++) |
| { |
| if ((pComp_params->m_mode5_rotation_mask & (1 << rotation)) == 0) |
| continue; |
| |
| memcpy(params5.m_weights, pParams->m_weights, sizeof(params5.m_weights)); |
| if (rotation) |
| swapu(¶ms5.m_weights[rotation - 1], ¶ms5.m_weights[3]); |
| |
| color_quad_i rot_pixels[16]; |
| const color_quad_i * pTrial_pixels = pPixels; |
| uint32_t trial_lo_a = lo_a, trial_hi_a = hi_a; |
| if (rotation) |
| { |
| trial_lo_a = 255; |
| trial_hi_a = 0; |
| |
| for ( uint32_t i = 0; i < 16; i++) |
| { |
| color_quad_i c = pPixels[i]; |
| swapi(&c.m_c[3], &c.m_c[rotation - 1]); |
| rot_pixels[i] = c; |
| |
| trial_lo_a = minimumu(trial_lo_a, c.m_c[3]); |
| trial_hi_a = maximumu(trial_hi_a, c.m_c[3]); |
| } |
| |
| pTrial_pixels = rot_pixels; |
| } |
| |
| bc7_optimization_results trial_opt_results5; |
| |
| uint64_t trial_mode5_err = 0; |
| |
| handle_alpha_block_mode5(pTrial_pixels, pComp_params, ¶ms5, trial_lo_a, trial_hi_a, &trial_opt_results5, &trial_mode5_err); |
| |
| if (trial_mode5_err < best_err) |
| { |
| best_err = trial_mode5_err; |
| |
| opt_results = trial_opt_results5; |
| opt_results.m_rotation = rotation; |
| } |
| } // rotation |
| } |
| |
| // Mode 7 |
| if (pComp_params->m_alpha_settings.m_use_mode7) |
| { |
| solution solutions[BC7E_MAX_PARTITIONS7]; |
| uint32_t num_solutions; |
| if (forced_partition >= 0) |
| { |
| solutions[0].m_index = forced_partition; |
| num_solutions = 1; |
| } |
| else |
| num_solutions = estimate_partition_list(7, pPixels, pComp_params, solutions, pComp_params->m_alpha_settings.m_max_mode7_partitions_to_try); |
| |
| color_cell_compressor_params params7 = *pParams; |
| |
| params7.m_weights[0] *= pComp_params->m_alpha_settings.m_mode67_error_weight_mul[0]; |
| params7.m_weights[1] *= pComp_params->m_alpha_settings.m_mode67_error_weight_mul[1]; |
| params7.m_weights[2] *= pComp_params->m_alpha_settings.m_mode67_error_weight_mul[2]; |
| params7.m_weights[3] *= pComp_params->m_alpha_settings.m_mode67_error_weight_mul[3]; |
| |
| params7.m_pSelector_weights = g_bc7_weights2; |
| params7.m_pSelector_weightsx = (const vec4F *)&g_bc7_weights2x[0]; |
| params7.m_num_selector_weights = 4; |
| |
| params7.m_comp_bits = 5; |
| params7.m_has_pbits = true; |
| params7.m_endpoints_share_pbit = false; |
| |
| params7.m_has_alpha = true; |
| |
| int selectors_temp[16]; |
| |
| const bool disable_faster_part_selection = false; |
| |
| for ( uint32_t solution_index = 0; solution_index < num_solutions; solution_index++) |
| { |
| const uint32_t trial_partition = solutions[solution_index].m_index; |
| assert(trial_partition < 64); |
| |
| const int *pPartition = &g_bc7_partition2[trial_partition * 16]; |
| |
| color_quad_i subset_colors[2][16]; |
| |
| uint32_t subset_total_colors7[2]; |
| subset_total_colors7[0] = 0; |
| subset_total_colors7[1] = 0; |
| |
| int subset_pixel_index7[2][16]; |
| int subset_selectors7[2][16]; |
| color_cell_compressor_results subset_results7[2]; |
| |
| for ( uint32_t idx = 0; idx < 16; idx++) |
| { |
| const uint32_t p = pPartition[idx]; |
| assert(p < 2); |
| |
| subset_colors[p][subset_total_colors7[p]] = pPixels[idx]; |
| subset_pixel_index7[p][subset_total_colors7[p]] = idx; |
| subset_total_colors7[p]++; |
| } |
| |
| uint64_t trial_err = 0; |
| for ( uint32_t subset = 0; subset < 2; subset++) |
| { |
| color_cell_compressor_results * pResults = &subset_results7[subset]; |
| |
| pResults->m_pSelectors = &subset_selectors7[subset][0]; |
| pResults->m_pSelectors_temp = selectors_temp; |
| |
| uint64_t err = color_cell_compression(7, ¶ms7, pResults, pComp_params, subset_total_colors7[subset], &subset_colors[subset][0], (num_solutions <= 2) || disable_faster_part_selection); |
| assert(err == pResults->m_best_overall_err); |
| |
| trial_err += err; |
| if (trial_err > best_err) |
| break; |
| } // subset |
| |
| if (trial_err < best_err) |
| { |
| best_err = trial_err; |
| |
| opt_results.m_mode = 7; |
| opt_results.m_index_selector = 0; |
| opt_results.m_rotation = 0; |
| opt_results.m_partition = trial_partition; |
| |
| for ( uint32_t subset = 0; subset < 2; subset++) |
| { |
| for ( uint32_t i = 0; i < subset_total_colors7[subset]; i++) |
| { |
| const uint32_t pixel_index = subset_pixel_index7[subset][i]; |
| |
| opt_results.m_selectors[pixel_index] = subset_selectors7[subset][i]; |
| } |
| |
| opt_results.m_low[subset] = subset_results7[subset].m_low_endpoint; |
| opt_results.m_high[subset] = subset_results7[subset].m_high_endpoint; |
| |
| opt_results.m_pbits[subset][0] = subset_results7[subset].m_pbits[0]; |
| opt_results.m_pbits[subset][1] = subset_results7[subset].m_pbits[1]; |
| } |
| opt_results.m_used_lut = subset_results7[0].m_used_lut || subset_results7[1].m_used_lut; |
| } |
| |
| } // solution_index |
| |
| if ((num_solutions > 2) && (opt_results.m_mode == 7) && (!disable_faster_part_selection)) |
| { |
| const uint32_t trial_partition = opt_results.m_partition; |
| assert(trial_partition < 64); |
| |
| const int *pPartition = &g_bc7_partition2[trial_partition * 16]; |
| |
| color_quad_i subset_colors[2][16]; |
| |
| uint32_t subset_total_colors7[2]; |
| subset_total_colors7[0] = 0; |
| subset_total_colors7[1] = 0; |
| |
| int subset_pixel_index7[2][16]; |
| int subset_selectors7[2][16]; |
| color_cell_compressor_results subset_results7[2]; |
| |
| for ( uint32_t idx = 0; idx < 16; idx++) |
| { |
| const uint32_t p = pPartition[idx]; |
| assert(p < 2); |
| |
| subset_colors[p][subset_total_colors7[p]] = pPixels[idx]; |
| subset_pixel_index7[p][subset_total_colors7[p]] = idx; |
| subset_total_colors7[p]++; |
| } |
| |
| uint64_t trial_err = 0; |
| for ( uint32_t subset = 0; subset < 2; subset++) |
| { |
| color_cell_compressor_results * pResults = &subset_results7[subset]; |
| |
| pResults->m_pSelectors = &subset_selectors7[subset][0]; |
| pResults->m_pSelectors_temp = selectors_temp; |
| |
| uint64_t err = color_cell_compression(7, ¶ms7, pResults, pComp_params, subset_total_colors7[subset], &subset_colors[subset][0], true); |
| assert(err == pResults->m_best_overall_err); |
| |
| trial_err += err; |
| if (trial_err > best_err) |
| break; |
| } // subset |
| |
| if (trial_err < best_err) |
| { |
| best_err = trial_err; |
| |
| for ( uint32_t subset = 0; subset < 2; subset++) |
| { |
| for ( uint32_t i = 0; i < subset_total_colors7[subset]; i++) |
| { |
| const uint32_t pixel_index = subset_pixel_index7[subset][i]; |
| |
| opt_results.m_selectors[pixel_index] = subset_selectors7[subset][i]; |
| } |
| |
| opt_results.m_low[subset] = subset_results7[subset].m_low_endpoint; |
| opt_results.m_high[subset] = subset_results7[subset].m_high_endpoint; |
| |
| opt_results.m_pbits[subset][0] = subset_results7[subset].m_pbits[0]; |
| opt_results.m_pbits[subset][1] = subset_results7[subset].m_pbits[1]; |
| } |
| opt_results.m_used_lut = subset_results7[0].m_used_lut || subset_results7[1].m_used_lut; |
| } |
| } |
| } |
| |
| if (pBest_err) *pBest_err = best_err; |
| if (pUsed_lut) *pUsed_lut = opt_results.m_used_lut; |
| encode_bc7_block(pBlock, &opt_results); |
| } |
| |
| static void handle_opaque_block(void * pBlock, const color_quad_i * pPixels, const bc7e_compress_block_params * pComp_params, color_cell_compressor_params * pParams, int forced_partition = -1, uint64_t* pBest_err = nullptr, bool* pUsed_lut = nullptr) |
| { |
| int selectors_temp[16]; |
| |
| bc7_optimization_results opt_results = {}; |
| |
| uint64_t best_err = UINT64_MAX; |
| |
| // Mode 6 |
| if (pComp_params->m_opaque_settings.m_use_mode[6]) |
| { |
| pParams->m_pSelector_weights = g_bc7_weights4; |
| pParams->m_pSelector_weightsx = (const vec4F * )&g_bc7_weights4x[0]; |
| pParams->m_num_selector_weights = 16; |
| |
| pParams->m_comp_bits = 7; |
| pParams->m_has_pbits = true; |
| pParams->m_endpoints_share_pbit = false; |
| |
| pParams->m_perceptual = pComp_params->m_perceptual; |
| |
| color_cell_compressor_results results6; |
| results6.m_pSelectors = opt_results.m_selectors; |
| results6.m_pSelectors_temp = selectors_temp; |
| |
| best_err = color_cell_compression(6, pParams, &results6, pComp_params, 16, pPixels, true); |
| |
| opt_results.m_mode = 6; |
| opt_results.m_index_selector = 0; |
| opt_results.m_rotation = 0; |
| opt_results.m_partition = 0; |
| opt_results.m_used_lut = results6.m_used_lut; |
| |
| opt_results.m_low[0] = results6.m_low_endpoint; |
| opt_results.m_high[0] = results6.m_high_endpoint; |
| |
| opt_results.m_pbits[0][0] = results6.m_pbits[0]; |
| opt_results.m_pbits[0][1] = results6.m_pbits[1]; |
| } |
| |
| solution solutions2[BC7E_MAX_PARTITIONS3]; |
| uint32_t num_solutions2 = 0; |
| if (pComp_params->m_opaque_settings.m_use_mode[1] || pComp_params->m_opaque_settings.m_use_mode[3]) |
| { |
| if (forced_partition >= 0) |
| { |
| solutions2[0].m_index = forced_partition; |
| num_solutions2 = 1; |
| } |
| else if (pComp_params->m_opaque_settings.m_max_mode13_partitions_to_try == 1) |
| { |
| solutions2[0].m_index = estimate_partition(1, pPixels, pComp_params); |
| num_solutions2 = 1; |
| } |
| else |
| { |
| num_solutions2 = estimate_partition_list(1, pPixels, pComp_params, solutions2, pComp_params->m_opaque_settings.m_max_mode13_partitions_to_try); |
| } |
| } |
| |
| const bool disable_faster_part_selection = false; |
| |
| // Mode 1 |
| if (pComp_params->m_opaque_settings.m_use_mode[1]) |
| { |
| pParams->m_pSelector_weights = g_bc7_weights3; |
| pParams->m_pSelector_weightsx = (const vec4F *)&g_bc7_weights3x[0]; |
| pParams->m_num_selector_weights = 8; |
| |
| pParams->m_comp_bits = 6; |
| pParams->m_has_pbits = true; |
| pParams->m_endpoints_share_pbit = true; |
| |
| pParams->m_perceptual = pComp_params->m_perceptual; |
| |
| for ( uint32_t solution_index = 0; solution_index < num_solutions2; solution_index++) |
| { |
| const uint32_t trial_partition = solutions2[solution_index].m_index; |
| assert(trial_partition < 64); |
| |
| const int *pPartition = &g_bc7_partition2[trial_partition * 16]; |
| |
| color_quad_i subset_colors[2][16]; |
| |
| uint32_t subset_total_colors1[2]; |
| subset_total_colors1[0] = 0; |
| subset_total_colors1[1] = 0; |
| |
| int subset_pixel_index1[2][16]; |
| int subset_selectors1[2][16]; |
| color_cell_compressor_results subset_results1[2]; |
| |
| for ( uint32_t idx = 0; idx < 16; idx++) |
| { |
| const uint32_t p = pPartition[idx]; |
| assert(p < 2); |
| |
| subset_colors[p][subset_total_colors1[p]] = pPixels[idx]; |
| subset_pixel_index1[p][subset_total_colors1[p]] = idx; |
| subset_total_colors1[p]++; |
| } |
| |
| uint64_t trial_err = 0; |
| for ( uint32_t subset = 0; subset < 2; subset++) |
| { |
| color_cell_compressor_results * pResults = &subset_results1[subset]; |
| |
| pResults->m_pSelectors = &subset_selectors1[subset][0]; |
| pResults->m_pSelectors_temp = selectors_temp; |
| |
| uint64_t err = color_cell_compression(1, pParams, pResults, pComp_params, subset_total_colors1[subset], &subset_colors[subset][0], (num_solutions2 <= 2) || disable_faster_part_selection); |
| assert(err == pResults->m_best_overall_err); |
| |
| trial_err += err; |
| if (trial_err > best_err) |
| break; |
| |
| } // subset |
| |
| if (trial_err < best_err) |
| { |
| best_err = trial_err; |
| |
| opt_results.m_mode = 1; |
| opt_results.m_index_selector = 0; |
| opt_results.m_rotation = 0; |
| opt_results.m_partition = trial_partition; |
| opt_results.m_used_lut = subset_results1[0].m_used_lut || subset_results1[1].m_used_lut; |
| |
| for ( uint32_t subset = 0; subset < 2; subset++) |
| { |
| for ( uint32_t i = 0; i < subset_total_colors1[subset]; i++) |
| { |
| const uint32_t pixel_index = subset_pixel_index1[subset][i]; |
| |
| opt_results.m_selectors[pixel_index] = subset_selectors1[subset][i]; |
| } |
| |
| opt_results.m_low[subset] = subset_results1[subset].m_low_endpoint; |
| opt_results.m_high[subset] = subset_results1[subset].m_high_endpoint; |
| |
| opt_results.m_pbits[subset][0] = subset_results1[subset].m_pbits[0]; |
| } |
| } |
| } |
| |
| if ((num_solutions2 > 2) && (opt_results.m_mode == 1) && (!disable_faster_part_selection)) |
| { |
| const uint32_t trial_partition = opt_results.m_partition; |
| assert(trial_partition < 64); |
| |
| const int *pPartition = &g_bc7_partition2[trial_partition * 16]; |
| |
| color_quad_i subset_colors[2][16]; |
| |
| uint32_t subset_total_colors1[2]; |
| subset_total_colors1[0] = 0; |
| subset_total_colors1[1] = 0; |
| |
| int subset_pixel_index1[2][16]; |
| int subset_selectors1[2][16]; |
| color_cell_compressor_results subset_results1[2]; |
| |
| for ( uint32_t idx = 0; idx < 16; idx++) |
| { |
| const uint32_t p = pPartition[idx]; |
| assert(p < 2); |
| |
| subset_colors[p][subset_total_colors1[p]] = pPixels[idx]; |
| subset_pixel_index1[p][subset_total_colors1[p]] = idx; |
| subset_total_colors1[p]++; |
| } |
| |
| uint64_t trial_err = 0; |
| for ( uint32_t subset = 0; subset < 2; subset++) |
| { |
| color_cell_compressor_results * pResults = &subset_results1[subset]; |
| |
| pResults->m_pSelectors = &subset_selectors1[subset][0]; |
| pResults->m_pSelectors_temp = selectors_temp; |
| |
| uint64_t err = color_cell_compression(1, pParams, pResults, pComp_params, subset_total_colors1[subset], &subset_colors[subset][0], true); |
| assert(err == pResults->m_best_overall_err); |
| |
| trial_err += err; |
| if (trial_err > best_err) |
| break; |
| |
| } // subset |
| |
| if (trial_err < best_err) |
| { |
| best_err = trial_err; |
| opt_results.m_used_lut = subset_results1[0].m_used_lut || subset_results1[1].m_used_lut; |
| |
| for ( uint32_t subset = 0; subset < 2; subset++) |
| { |
| for ( uint32_t i = 0; i < subset_total_colors1[subset]; i++) |
| { |
| const uint32_t pixel_index = subset_pixel_index1[subset][i]; |
| opt_results.m_selectors[pixel_index] = subset_selectors1[subset][i]; |
| } |
| |
| opt_results.m_low[subset] = subset_results1[subset].m_low_endpoint; |
| opt_results.m_high[subset] = subset_results1[subset].m_high_endpoint; |
| |
| opt_results.m_pbits[subset][0] = subset_results1[subset].m_pbits[0]; |
| } |
| } |
| } |
| } |
| |
| // Mode 0 |
| if (pComp_params->m_opaque_settings.m_use_mode[0]) |
| { |
| solution solutions3[BC7E_MAX_PARTITIONS0]; |
| uint32_t num_solutions3 = 0; |
| if (forced_partition >= 0) |
| { |
| solutions3[0].m_index = forced_partition; |
| num_solutions3 = 1; |
| } |
| else if (pComp_params->m_opaque_settings.m_max_mode0_partitions_to_try == 1) |
| { |
| solutions3[0].m_index = estimate_partition(0, pPixels, pComp_params); |
| num_solutions3 = 1; |
| } |
| else |
| { |
| num_solutions3 = estimate_partition_list(0, pPixels, pComp_params, solutions3, pComp_params->m_opaque_settings.m_max_mode0_partitions_to_try); |
| } |
| |
| pParams->m_pSelector_weights = g_bc7_weights3; |
| pParams->m_pSelector_weightsx = (const vec4F *)&g_bc7_weights3x[0]; |
| pParams->m_num_selector_weights = 8; |
| |
| pParams->m_comp_bits = 4; |
| pParams->m_has_pbits = true; |
| pParams->m_endpoints_share_pbit = false; |
| |
| pParams->m_perceptual = pComp_params->m_perceptual; |
| |
| for ( uint32_t solution_index = 0; solution_index < num_solutions3; solution_index++) |
| { |
| const uint32_t best_partition0 = solutions3[solution_index].m_index; |
| |
| const int *pPartition = &g_bc7_partition3[best_partition0 * 16]; |
| |
| color_quad_i subset_colors[3][16]; |
| |
| uint32_t subset_total_colors0[3]; |
| subset_total_colors0[0] = 0; |
| subset_total_colors0[1] = 0; |
| subset_total_colors0[2] = 0; |
| |
| int subset_pixel_index0[3][16]; |
| |
| for ( uint32_t idx = 0; idx < 16; idx++) |
| { |
| const uint32_t p = pPartition[idx]; |
| |
| subset_colors[p][subset_total_colors0[p]] = pPixels[idx]; |
| subset_pixel_index0[p][subset_total_colors0[p]] = idx; |
| subset_total_colors0[p]++; |
| } |
| |
| color_cell_compressor_results subset_results0[3]; |
| int subset_selectors0[3][16]; |
| |
| uint64_t mode0_err = 0; |
| for ( uint32_t subset = 0; subset < 3; subset++) |
| { |
| color_cell_compressor_results * pResults = &subset_results0[subset]; |
| |
| pResults->m_pSelectors = &subset_selectors0[subset][0]; |
| pResults->m_pSelectors_temp = selectors_temp; |
| |
| uint64_t err = color_cell_compression(0, pParams, pResults, pComp_params, subset_total_colors0[subset], &subset_colors[subset][0], true); |
| assert(err == pResults->m_best_overall_err); |
| |
| mode0_err += err; |
| if (mode0_err > best_err) |
| break; |
| } // subset |
| |
| if (mode0_err < best_err) |
| { |
| best_err = mode0_err; |
| |
| opt_results.m_mode = 0; |
| opt_results.m_index_selector = 0; |
| opt_results.m_rotation = 0; |
| opt_results.m_partition = best_partition0; |
| |
| for ( uint32_t subset = 0; subset < 3; subset++) |
| { |
| for ( uint32_t i = 0; i < subset_total_colors0[subset]; i++) |
| { |
| const uint32_t pixel_index = subset_pixel_index0[subset][i]; |
| |
| opt_results.m_selectors[pixel_index] = subset_selectors0[subset][i]; |
| } |
| |
| opt_results.m_low[subset] = subset_results0[subset].m_low_endpoint; |
| opt_results.m_high[subset] = subset_results0[subset].m_high_endpoint; |
| |
| opt_results.m_pbits[subset][0] = subset_results0[subset].m_pbits[0]; |
| opt_results.m_pbits[subset][1] = subset_results0[subset].m_pbits[1]; |
| } |
| opt_results.m_used_lut = subset_results0[0].m_used_lut || subset_results0[1].m_used_lut || subset_results0[2].m_used_lut; |
| } |
| } |
| } |
| |
| // Mode 3 |
| if (pComp_params->m_opaque_settings.m_use_mode[3]) |
| { |
| pParams->m_pSelector_weights = g_bc7_weights2; |
| pParams->m_pSelector_weightsx = (const vec4F *)&g_bc7_weights2x[0]; |
| pParams->m_num_selector_weights = 4; |
| |
| pParams->m_comp_bits = 7; |
| pParams->m_has_pbits = true; |
| pParams->m_endpoints_share_pbit = false; |
| |
| pParams->m_perceptual = pComp_params->m_perceptual; |
| |
| for ( uint32_t solution_index = 0; solution_index < num_solutions2; solution_index++) |
| { |
| const uint32_t trial_partition = solutions2[solution_index].m_index; |
| assert(trial_partition < 64); |
| |
| const int *pPartition = &g_bc7_partition2[trial_partition * 16]; |
| |
| color_quad_i subset_colors[2][16]; |
| |
| uint32_t subset_total_colors3[2]; |
| subset_total_colors3[0] = 0; |
| subset_total_colors3[1] = 0; |
| |
| int subset_pixel_index3[2][16]; |
| int subset_selectors3[2][16]; |
| color_cell_compressor_results subset_results3[2]; |
| |
| for ( uint32_t idx = 0; idx < 16; idx++) |
| { |
| const uint32_t p = pPartition[idx]; |
| assert(p < 2); |
| |
| subset_colors[p][subset_total_colors3[p]] = pPixels[idx]; |
| |
| subset_pixel_index3[p][subset_total_colors3[p]] = idx; |
| |
| subset_total_colors3[p]++; |
| } |
| |
| uint64_t trial_err = 0; |
| for ( uint32_t subset = 0; subset < 2; subset++) |
| { |
| color_cell_compressor_results * pResults = &subset_results3[subset]; |
| |
| pResults->m_pSelectors = &subset_selectors3[subset][0]; |
| pResults->m_pSelectors_temp = selectors_temp; |
| |
| uint64_t err = color_cell_compression(3, pParams, pResults, pComp_params, subset_total_colors3[subset], &subset_colors[subset][0], (num_solutions2 <= 2) || disable_faster_part_selection); |
| assert(err == pResults->m_best_overall_err); |
| |
| trial_err += err; |
| if (trial_err > best_err) |
| break; |
| } // subset |
| |
| if (trial_err < best_err) |
| { |
| best_err = trial_err; |
| |
| opt_results.m_mode = 3; |
| opt_results.m_index_selector = 0; |
| opt_results.m_rotation = 0; |
| opt_results.m_partition = trial_partition; |
| |
| for ( uint32_t subset = 0; subset < 2; subset++) |
| { |
| for ( uint32_t i = 0; i < subset_total_colors3[subset]; i++) |
| { |
| const uint32_t pixel_index = subset_pixel_index3[subset][i]; |
| |
| opt_results.m_selectors[pixel_index] = subset_selectors3[subset][i]; |
| } |
| |
| opt_results.m_low[subset] = subset_results3[subset].m_low_endpoint; |
| opt_results.m_high[subset] = subset_results3[subset].m_high_endpoint; |
| |
| opt_results.m_pbits[subset][0] = subset_results3[subset].m_pbits[0]; |
| opt_results.m_pbits[subset][1] = subset_results3[subset].m_pbits[1]; |
| } |
| opt_results.m_used_lut = subset_results3[0].m_used_lut || subset_results3[1].m_used_lut; |
| } |
| |
| } // solution_index |
| |
| if ((num_solutions2 > 2) && (opt_results.m_mode == 3) && (!disable_faster_part_selection)) |
| { |
| const uint32_t trial_partition = opt_results.m_partition; |
| assert(trial_partition < 64); |
| |
| const int *pPartition = &g_bc7_partition2[trial_partition * 16]; |
| |
| color_quad_i subset_colors[2][16]; |
| |
| uint32_t subset_total_colors3[2]; |
| subset_total_colors3[0] = 0; |
| subset_total_colors3[1] = 0; |
| |
| int subset_pixel_index3[2][16]; |
| int subset_selectors3[2][16]; |
| color_cell_compressor_results subset_results3[2]; |
| |
| for ( uint32_t idx = 0; idx < 16; idx++) |
| { |
| const uint32_t p = pPartition[idx]; |
| assert(p < 2); |
| |
| subset_colors[p][subset_total_colors3[p]] = pPixels[idx]; |
| |
| subset_pixel_index3[p][subset_total_colors3[p]] = idx; |
| |
| subset_total_colors3[p]++; |
| } |
| |
| uint64_t trial_err = 0; |
| for ( uint32_t subset = 0; subset < 2; subset++) |
| { |
| color_cell_compressor_results * pResults = &subset_results3[subset]; |
| |
| pResults->m_pSelectors = &subset_selectors3[subset][0]; |
| pResults->m_pSelectors_temp = selectors_temp; |
| |
| uint64_t err = color_cell_compression(3, pParams, pResults, pComp_params, subset_total_colors3[subset], &subset_colors[subset][0], true); |
| assert(err == pResults->m_best_overall_err); |
| |
| trial_err += err; |
| if (trial_err > best_err) |
| break; |
| } // subset |
| |
| if (trial_err < best_err) |
| { |
| best_err = trial_err; |
| |
| for ( uint32_t subset = 0; subset < 2; subset++) |
| { |
| for ( uint32_t i = 0; i < subset_total_colors3[subset]; i++) |
| { |
| const uint32_t pixel_index = subset_pixel_index3[subset][i]; |
| |
| opt_results.m_selectors[pixel_index] = subset_selectors3[subset][i]; |
| } |
| |
| opt_results.m_low[subset] = subset_results3[subset].m_low_endpoint; |
| opt_results.m_high[subset] = subset_results3[subset].m_high_endpoint; |
| |
| opt_results.m_pbits[subset][0] = subset_results3[subset].m_pbits[0]; |
| opt_results.m_pbits[subset][1] = subset_results3[subset].m_pbits[1]; |
| } |
| opt_results.m_used_lut = subset_results3[0].m_used_lut || subset_results3[1].m_used_lut; |
| } |
| } |
| } |
| |
| // Mode 5 |
| if ((!pComp_params->m_perceptual) && (pComp_params->m_opaque_settings.m_use_mode[5])) |
| { |
| color_cell_compressor_params params5 = *pParams; |
| |
| for ( uint32_t rotation = 0; rotation < 4; rotation++) |
| { |
| if ((pComp_params->m_mode5_rotation_mask & (1 << rotation)) == 0) |
| continue; |
| |
| memcpy(params5.m_weights, pParams->m_weights, sizeof(params5.m_weights)); |
| if (rotation) |
| swapu(¶ms5.m_weights[rotation - 1], ¶ms5.m_weights[3]); |
| |
| color_quad_i rot_pixels[16]; |
| const color_quad_i * pTrial_pixels = pPixels; |
| uint32_t trial_lo_a = 255, trial_hi_a = 255; |
| if (rotation) |
| { |
| trial_lo_a = 255; |
| trial_hi_a = 0; |
| |
| for ( uint32_t i = 0; i < 16; i++) |
| { |
| color_quad_i c = pPixels[i]; |
| swapi(&c.m_c[3], &c.m_c[rotation - 1]); |
| rot_pixels[i] = c; |
| |
| trial_lo_a = minimumu(trial_lo_a, c.m_c[3]); |
| trial_hi_a = maximumu(trial_hi_a, c.m_c[3]); |
| } |
| |
| pTrial_pixels = rot_pixels; |
| } |
| |
| bc7_optimization_results trial_opt_results5; |
| |
| uint64_t trial_mode5_err = 0; |
| |
| handle_alpha_block_mode5(pTrial_pixels, pComp_params, ¶ms5, trial_lo_a, trial_hi_a, &trial_opt_results5, &trial_mode5_err); |
| |
| if (trial_mode5_err < best_err) |
| { |
| best_err = trial_mode5_err; |
| |
| opt_results = trial_opt_results5; |
| opt_results.m_rotation = rotation; |
| } |
| } // rotation |
| } |
| |
| // Mode 2 |
| if (pComp_params->m_opaque_settings.m_use_mode[2]) |
| { |
| solution solutions3[BC7E_MAX_PARTITIONS2]; |
| uint32_t num_solutions3 = 0; |
| if (forced_partition >= 0) |
| { |
| solutions3[0].m_index = forced_partition; |
| num_solutions3 = 1; |
| } |
| else if (pComp_params->m_opaque_settings.m_max_mode2_partitions_to_try == 1) |
| { |
| solutions3[0].m_index = estimate_partition(2, pPixels, pComp_params); |
| num_solutions3 = 1; |
| } |
| else |
| { |
| num_solutions3 = estimate_partition_list(2, pPixels, pComp_params, solutions3, pComp_params->m_opaque_settings.m_max_mode2_partitions_to_try); |
| } |
| |
| pParams->m_pSelector_weights = g_bc7_weights2; |
| pParams->m_pSelector_weightsx = (const vec4F *)&g_bc7_weights2x[0]; |
| pParams->m_num_selector_weights = 4; |
| |
| pParams->m_comp_bits = 5; |
| pParams->m_has_pbits = false; |
| pParams->m_endpoints_share_pbit = false; |
| |
| pParams->m_perceptual = pComp_params->m_perceptual; |
| |
| for ( uint32_t solution_index = 0; solution_index < num_solutions3; solution_index++) |
| { |
| const int32_t best_partition2 = solutions3[solution_index].m_index; |
| |
| uint32_t subset_total_colors2[3]; |
| subset_total_colors2[0] = 0; |
| subset_total_colors2[1] = 0; |
| subset_total_colors2[2] = 0; |
| |
| int subset_pixel_index2[3][16]; |
| |
| const int *pPartition = &g_bc7_partition3[best_partition2 * 16]; |
| |
| color_quad_i subset_colors[3][16]; |
| |
| for ( uint32_t idx = 0; idx < 16; idx++) |
| { |
| const uint32_t p = pPartition[idx]; |
| |
| subset_colors[p][subset_total_colors2[p]] = pPixels[idx]; |
| |
| subset_pixel_index2[p][subset_total_colors2[p]] = idx; |
| |
| subset_total_colors2[p]++; |
| } |
| |
| int subset_selectors2[3][16]; |
| color_cell_compressor_results subset_results2[3]; |
| |
| uint64_t mode2_err = 0; |
| for ( uint32_t subset = 0; subset < 3; subset++) |
| { |
| color_cell_compressor_results * pResults = &subset_results2[subset]; |
| |
| pResults->m_pSelectors = &subset_selectors2[subset][0]; |
| pResults->m_pSelectors_temp = selectors_temp; |
| |
| uint64_t err = color_cell_compression(2, pParams, pResults, pComp_params, subset_total_colors2[subset], &subset_colors[subset][0], true); |
| assert(err == pResults->m_best_overall_err); |
| |
| mode2_err += err; |
| if (mode2_err > best_err) |
| break; |
| } // subset |
| |
| if (mode2_err < best_err) |
| { |
| best_err = mode2_err; |
| |
| opt_results.m_mode = 2; |
| opt_results.m_index_selector = 0; |
| opt_results.m_rotation = 0; |
| opt_results.m_partition = best_partition2; |
| |
| for ( uint32_t subset = 0; subset < 3; subset++) |
| { |
| for ( uint32_t i = 0; i < subset_total_colors2[subset]; i++) |
| { |
| const uint32_t pixel_index = subset_pixel_index2[subset][i]; |
| |
| opt_results.m_selectors[pixel_index] = subset_selectors2[subset][i]; |
| } |
| |
| opt_results.m_low[subset] = subset_results2[subset].m_low_endpoint; |
| opt_results.m_high[subset] = subset_results2[subset].m_high_endpoint; |
| } |
| opt_results.m_used_lut = subset_results2[0].m_used_lut || subset_results2[1].m_used_lut || subset_results2[2].m_used_lut; |
| } |
| } |
| } |
| |
| // Mode 4 |
| if ((!pComp_params->m_perceptual) && (pComp_params->m_opaque_settings.m_use_mode[4])) |
| { |
| color_cell_compressor_params params4 = *pParams; |
| |
| for ( uint32_t rotation = 0; rotation < 4; rotation++) |
| { |
| if ((pComp_params->m_mode4_rotation_mask & (1 << rotation)) == 0) |
| continue; |
| |
| memcpy(params4.m_weights, pParams->m_weights, sizeof(params4.m_weights)); |
| if (rotation) |
| swapu(¶ms4.m_weights[rotation - 1], ¶ms4.m_weights[3]); |
| |
| color_quad_i rot_pixels[16]; |
| const color_quad_i * pTrial_pixels = pPixels; |
| uint32_t trial_lo_a = 255, trial_hi_a = 255; |
| if (rotation) |
| { |
| trial_lo_a = 255; |
| trial_hi_a = 0; |
| |
| for ( uint32_t i = 0; i < 16; i++) |
| { |
| color_quad_i c = pPixels[i]; |
| swapi(&c.m_c[3], &c.m_c[rotation - 1]); |
| rot_pixels[i] = c; |
| |
| trial_lo_a = minimumu(trial_lo_a, c.m_c[3]); |
| trial_hi_a = maximumu(trial_hi_a, c.m_c[3]); |
| } |
| |
| pTrial_pixels = rot_pixels; |
| } |
| |
| bc7_optimization_results trial_opt_results4; |
| |
| uint64_t trial_mode4_err = best_err; |
| |
| handle_alpha_block_mode4(pTrial_pixels, pComp_params, ¶ms4, trial_lo_a, trial_hi_a, &trial_opt_results4, &trial_mode4_err); |
| |
| if (trial_mode4_err < best_err) |
| { |
| best_err = trial_mode4_err; |
| |
| opt_results.m_mode = 4; |
| opt_results.m_index_selector = trial_opt_results4.m_index_selector; |
| opt_results.m_rotation = rotation; |
| opt_results.m_partition = 0; |
| opt_results.m_used_lut = trial_opt_results4.m_used_lut; |
| |
| opt_results.m_low[0] = trial_opt_results4.m_low[0]; |
| opt_results.m_high[0] = trial_opt_results4.m_high[0]; |
| |
| for ( uint32_t i = 0; i < 16; i++) |
| opt_results.m_selectors[i] = trial_opt_results4.m_selectors[i]; |
| |
| for ( uint32_t i = 0; i < 16; i++) |
| opt_results.m_alpha_selectors[i] = trial_opt_results4.m_alpha_selectors[i]; |
| } |
| } // rotation |
| } |
| |
| if (pBest_err) *pBest_err = best_err; |
| if (pUsed_lut) *pUsed_lut = opt_results.m_used_lut; |
| encode_bc7_block(pBlock, &opt_results); |
| } |
| |
| // all solid color blocks can be 100% perfectly encoded with just mode 5 |
| static void handle_block_solid(void * pBlock, uint32_t cr, uint32_t cg, uint32_t cb, uint32_t ca, bool* pUsed_lut = nullptr) |
| { |
| if (pUsed_lut) *pUsed_lut = true; // solid blocks always use the mode-5 optimal-endpoint LUT |
| uint32_t er = g_bc7_mode_5_optimal_endpoints[cr]; |
| uint32_t eg = g_bc7_mode_5_optimal_endpoints[cg]; |
| uint32_t eb = g_bc7_mode_5_optimal_endpoints[cb]; |
| color_quad_i lp, hp; |
| color_quad_i_set(&lp, er & 0xFF, eg & 0xFF, eb & 0xFF, ca); |
| color_quad_i_set(&hp, er >> 8, eg >> 8, eb >> 8, ca); |
| |
| bc7_optimization_results opt; |
| opt.m_mode = 5; |
| opt.m_low[0] = lp; |
| opt.m_high[0] = hp; |
| opt.m_pbits[0][0] = 0; |
| opt.m_pbits[0][1] = 0; |
| opt.m_index_selector = 0; |
| opt.m_rotation = 0; |
| opt.m_partition = 0; |
| for ( int i = 0; i < 16; ++i) |
| opt.m_selectors[i] = BC7E_MODE_5_OPTIMAL_INDEX; |
| for ( int i = 0; i < 16; ++i) |
| opt.m_alpha_selectors[i] = 0; |
| encode_bc7_block(pBlock, &opt); |
| } |
| |
| static void handle_opaque_block_mode6(void * pBlock, const color_quad_i * pPixels, const bc7e_compress_block_params * pComp_params, color_cell_compressor_params * pParams, bool* pUsed_lut = nullptr) |
| { |
| int selectors_temp[16]; |
| |
| bc7_optimization_results opt_results = {}; |
| |
| uint64_t best_err = UINT64_MAX; |
| |
| // Mode 6 |
| pParams->m_pSelector_weights = g_bc7_weights4; |
| pParams->m_pSelector_weightsx = (const vec4F * )&g_bc7_weights4x[0]; |
| pParams->m_num_selector_weights = 16; |
| |
| pParams->m_comp_bits = 7; |
| pParams->m_has_pbits = true; |
| pParams->m_endpoints_share_pbit = false; |
| |
| pParams->m_perceptual = pComp_params->m_perceptual; |
| |
| color_cell_compressor_results results6; |
| results6.m_pSelectors = opt_results.m_selectors; |
| results6.m_pSelectors_temp = selectors_temp; |
| |
| best_err = color_cell_compression(6, pParams, &results6, pComp_params, 16, pPixels, true); |
| NOTE_UNUSED(best_err); // mode 6 here is unconditional; the returned error isn't compared - silence clang -Wunused-but-set-variable |
| |
| opt_results.m_mode = 6; |
| opt_results.m_index_selector = 0; |
| opt_results.m_rotation = 0; |
| opt_results.m_partition = 0; |
| |
| opt_results.m_low[0] = results6.m_low_endpoint; |
| opt_results.m_high[0] = results6.m_high_endpoint; |
| |
| opt_results.m_pbits[0][0] = results6.m_pbits[0]; |
| opt_results.m_pbits[0][1] = results6.m_pbits[1]; |
| |
| if (pUsed_lut) *pUsed_lut = results6.m_used_lut; |
| encode_bc7_block_mode6(pBlock, &opt_results); |
| } |
| |
| // Compress a single 4x4 RGBA block to ONE specified BC7 mode only. Additive API: it does |
| // not touch bc7e_compress_blocks(). Writes the 16-byte block to pBlock and returns its error. |
| // mode : 0..7 |
| // partition : modes 0,1,2,3,7 -> partition pattern index, or -1 to auto-select the |
| // optimal one (existing estimate_partition* logic). Ignored for 4,5,6. |
| // rotation : modes 4,5 -> dual-plane component rotation [0..3] (0 = none). |
| // index_selector : mode 4 -> 0 or 1 (which index set is the scalar channel). |
| // Forcing an opaque mode (0-3) on a block with alpha drops alpha (those modes force A=255). |
| uint64_t bc7e_compress_block_single_mode(uint64_t* pBlock, const uint32_t* pPixelsRGBA, const bc7e_compress_block_params* pComp_params, |
| uint32_t mode, int partition, uint32_t rotation, uint32_t index_selector) |
| { |
| assert(g_codec_initialized); |
| assert(mode <= 7); |
| |
| // Extract the 16 pixels + alpha range (mirrors bc7e_compress_blocks). |
| const color_quad_u8* pSrcPixels = (const color_quad_u8*)pPixelsRGBA; |
| color_quad_i temp_pixels[16]; |
| int lo_a = 255, hi_a = 0; |
| for (uint32_t i = 0; i < 16; i++) |
| { |
| color_quad_u8 c = pSrcPixels[i]; |
| temp_pixels[i].m_c[0] = c.m_c[0]; |
| temp_pixels[i].m_c[1] = c.m_c[1]; |
| temp_pixels[i].m_c[2] = c.m_c[2]; |
| temp_pixels[i].m_c[3] = c.m_c[3]; |
| lo_a = min(lo_a, (int)c.m_c[3]); |
| hi_a = max(hi_a, (int)c.m_c[3]); |
| } |
| |
| color_cell_compressor_params ccparams; |
| color_cell_compressor_params_clear(&ccparams); |
| memcpy(ccparams.m_weights, pComp_params->m_weights, sizeof(ccparams.m_weights)); |
| |
| // Single-mode params: copy caller's settings, disable every mode, enable only this one. |
| bc7e_compress_block_params p = *pComp_params; |
| for (uint32_t i = 0; i < 7; i++) |
| p.m_opaque_settings.m_use_mode[i] = false; |
| p.m_alpha_settings.m_use_mode4 = false; |
| p.m_alpha_settings.m_use_mode5 = false; |
| p.m_alpha_settings.m_use_mode6 = false; |
| p.m_alpha_settings.m_use_mode7 = false; |
| p.m_mode6_only = false; |
| |
| // Validate/clamp a caller-supplied partition to the mode's valid range; -1 means auto-select. |
| // Partition field width: mode 0 is 4 bits (16 patterns); modes 1,2,3,7 are 6 bits (64 patterns); |
| // modes 4,5,6 have no partition field. |
| int forced_partition = partition; |
| if (forced_partition >= 0) |
| { |
| const int num_partitions = 1 << g_bc7_partition_bits[mode]; // 1 for non-partitioned modes |
| assert((num_partitions > 1) && "partition index supplied for a non-partitioned BC7 mode (4/5/6)"); |
| assert((forced_partition < num_partitions) && "partition index out of range for this BC7 mode (mode 0: 0-15, modes 1/2/3/7: 0-63)"); |
| if (num_partitions <= 1) |
| forced_partition = -1; // mode has no partition field; ignore |
| else if (forced_partition >= num_partitions) |
| forced_partition = num_partitions - 1; |
| } |
| |
| uint64_t best_err = UINT64_MAX; |
| |
| if (mode <= 3) |
| { |
| // Opaque-only modes (alpha dropped / forced to 255). |
| p.m_opaque_settings.m_use_mode[mode] = true; |
| handle_opaque_block(pBlock, temp_pixels, &p, &ccparams, forced_partition, &best_err); |
| } |
| else if ((mode == 4) || (mode == 5)) |
| { |
| // A non-zero rotation is only reachable under the handler's linear-metric gate, so |
| // force linear when one is explicitly requested (keeps the result valid + deterministic). |
| if (rotation != 0) |
| p.m_perceptual = false; |
| |
| if (mode == 4) |
| { |
| p.m_alpha_settings.m_use_mode4 = true; |
| p.m_alpha_settings.m_use_mode4_rotation = true; |
| p.m_mode4_rotation_mask = 1u << (rotation & 3); |
| p.m_mode4_index_mask = 1u << (index_selector & 1); |
| } |
| else |
| { |
| p.m_alpha_settings.m_use_mode5 = true; |
| p.m_alpha_settings.m_use_mode5_rotation = true; |
| p.m_mode5_rotation_mask = 1u << (rotation & 3); |
| } |
| handle_alpha_block(pBlock, temp_pixels, &p, &ccparams, (uint32_t)lo_a, (uint32_t)hi_a, -1, &best_err); |
| } |
| else if (mode == 6) |
| { |
| p.m_alpha_settings.m_use_mode6 = true; |
| handle_alpha_block(pBlock, temp_pixels, &p, &ccparams, (uint32_t)lo_a, (uint32_t)hi_a, -1, &best_err); |
| } |
| else // mode == 7 |
| { |
| p.m_alpha_settings.m_use_mode7 = true; |
| handle_alpha_block(pBlock, temp_pixels, &p, &ccparams, (uint32_t)lo_a, (uint32_t)hi_a, forced_partition, &best_err); |
| } |
| |
| return best_err; |
| } |
| |
| void bc7e_compress_blocks( uint32_t num_blocks, uint64_t * pBlocks, const uint32_t * pPixelsRGBA, const bc7e_compress_block_params * pComp_params, uint8_t * pUsed_lut) |
| { |
| if (!g_codec_initialized) |
| { |
| // Caller has forgotten to initialize the codec, or another thread is still working on that. We can't continue. |
| // What do we do here? |
| assert(0); |
| |
| memset(pBlocks, 0, num_blocks * 16); |
| return; |
| } |
| |
| color_cell_compressor_params params; |
| color_cell_compressor_params_clear(¶ms); |
| |
| memcpy(params.m_weights, pComp_params->m_weights, sizeof(params.m_weights)); |
| |
| assert(pComp_params->m_mode4_rotation_mask != 0); |
| assert(pComp_params->m_mode4_index_mask != 0); |
| assert(pComp_params->m_mode5_rotation_mask != 0); |
| assert(pComp_params->m_uber1_mask != 0); |
| |
| for (int32_t block_index = 0; block_index < (int32_t)num_blocks; block_index++) |
| { |
| const color_quad_u8 * pSrcPixels = &((const color_quad_u8 *)(pPixelsRGBA))[block_index * 16]; |
| |
| color_quad_i temp_pixels[16]; |
| |
| int lo_r = 255, hi_r = 0; |
| int lo_g = 255, hi_g = 0; |
| int lo_b = 255, hi_b = 0; |
| float lo_a = 255, hi_a = 0; |
| |
| for ( uint32_t i = 0; i < 16; i++) |
| { |
| color_quad_u8 c = pSrcPixels[i]; |
| |
| int r = c.m_c[0]; |
| int g = c.m_c[1]; |
| int b = c.m_c[2]; |
| int a = c.m_c[3]; |
| |
| temp_pixels[i].m_c[0] = r; |
| temp_pixels[i].m_c[1] = g; |
| temp_pixels[i].m_c[2] = b; |
| temp_pixels[i].m_c[3] = a; |
| |
| lo_r = min(lo_r, r); hi_r = max(hi_r, r); |
| lo_g = min(lo_g, g); hi_g = max(hi_g, g); |
| lo_b = min(lo_b, b); hi_b = max(hi_b, b); |
| |
| float fa = (float)(a); |
| |
| lo_a = min(lo_a, fa); |
| hi_a = max(hi_a, fa); |
| } |
| |
| bool all_same = lo_r==hi_r && lo_g==hi_g && lo_b==hi_b && lo_a==hi_a; |
| |
| uint64_t * pBlock = &pBlocks[block_index * 2]; |
| |
| bool block_used_lut = false; |
| |
| cif (all_same) |
| handle_block_solid(pBlock, lo_r, lo_g, lo_b, float_to_uint8(lo_a), &block_used_lut); |
| else |
| { |
| const bool has_alpha = (lo_a < 255); |
| // TODO: alpha block mode 6 only |
| cif (has_alpha) |
| handle_alpha_block(pBlock, temp_pixels, pComp_params, ¶ms, (int)lo_a, (int)hi_a, -1, nullptr, &block_used_lut); |
| else |
| { |
| if (pComp_params->m_mode6_only) |
| handle_opaque_block_mode6(pBlock, temp_pixels, pComp_params, ¶ms, &block_used_lut); |
| else |
| handle_opaque_block(pBlock, temp_pixels, pComp_params, ¶ms, -1, nullptr, &block_used_lut); |
| } |
| } |
| |
| if (pUsed_lut) |
| pUsed_lut[block_index] = block_used_lut ? 1 : 0; |
| } |
| } |
| |
| void bc7e_compress_block_params_init(bc7e_compress_block_params * p, bool perceptual) |
| { |
| p->m_max_partitions_mode[0] = BC7E_MAX_PARTITIONS0; |
| p->m_max_partitions_mode[1] = BC7E_MAX_PARTITIONS1; |
| p->m_max_partitions_mode[2] = BC7E_MAX_PARTITIONS2; |
| p->m_max_partitions_mode[3] = BC7E_MAX_PARTITIONS3; |
| p->m_max_partitions_mode[4] = 0; |
| p->m_max_partitions_mode[5] = 0; |
| p->m_max_partitions_mode[6] = 0; |
| p->m_max_partitions_mode[7] = BC7E_MAX_PARTITIONS7; |
| |
| p->m_use_luts = true; // default ON, matching bc7e's original behavior; callers may disable (e.g. XBC7 turns it off below lossless Q) |
| |
| p->m_perceptual = perceptual; |
| if (perceptual) |
| { |
| p->m_weights[0] = 128; |
| p->m_weights[1] = 64; |
| p->m_weights[2] = 16; |
| p->m_weights[3] = 256; |
| } |
| else |
| { |
| p->m_weights[0] = 1; |
| p->m_weights[1] = 1; |
| p->m_weights[2] = 1; |
| p->m_weights[3] = 1; |
| } |
| |
| p->m_pbit_search = false; |
| p->m_mode6_only = false; |
| p->m_refinement_passes = 1; |
| p->m_mode4_rotation_mask = 0xF; |
| p->m_mode4_index_mask = 3; |
| p->m_mode5_rotation_mask = 0xF; |
| p->m_uber1_mask = 7; |
| for ( uint32_t i = 0; i < 7; i++) |
| p->m_opaque_settings.m_use_mode[i] = true; |
| p->m_opaque_settings.m_max_mode13_partitions_to_try = 1; |
| p->m_opaque_settings.m_max_mode0_partitions_to_try = 1; |
| p->m_opaque_settings.m_max_mode2_partitions_to_try = 1; |
| p->m_alpha_settings.m_use_mode4 = true; |
| p->m_alpha_settings.m_use_mode5 = true; |
| p->m_alpha_settings.m_use_mode6 = true; |
| p->m_alpha_settings.m_use_mode7 = true; |
| p->m_alpha_settings.m_use_mode4_rotation = true; |
| p->m_alpha_settings.m_use_mode5_rotation = true; |
| p->m_alpha_settings.m_max_mode7_partitions_to_try = 1; |
| p->m_alpha_settings.m_mode67_error_weight_mul[0] = 1; |
| p->m_alpha_settings.m_mode67_error_weight_mul[1] = 1; |
| p->m_alpha_settings.m_mode67_error_weight_mul[2] = 1; |
| p->m_alpha_settings.m_mode67_error_weight_mul[3] = 1; |
| p->m_uber_level = 0; |
| } |
| |
| void bc7e_compress_block_params_init_slowest(bc7e_compress_block_params * p, bool perceptual) |
| { |
| bc7e_compress_block_params_init(p, perceptual); |
| |
| p->m_opaque_settings.m_max_mode13_partitions_to_try = 4; |
| p->m_opaque_settings.m_max_mode0_partitions_to_try = 4; |
| p->m_opaque_settings.m_max_mode2_partitions_to_try = 4; |
| p->m_alpha_settings.m_max_mode7_partitions_to_try = 4; |
| |
| p->m_pbit_search = true; |
| p->m_uber_level = 4; |
| } |
| |
| void bc7e_compress_block_params_init_veryslow(bc7e_compress_block_params * p, bool perceptual) |
| { |
| bc7e_compress_block_params_init(p, perceptual); |
| |
| p->m_opaque_settings.m_max_mode13_partitions_to_try = 2; |
| p->m_opaque_settings.m_max_mode0_partitions_to_try = 2; |
| p->m_opaque_settings.m_max_mode2_partitions_to_try = 2; |
| p->m_alpha_settings.m_max_mode7_partitions_to_try = 2; |
| |
| p->m_pbit_search = true; |
| p->m_uber_level = 2; |
| } |
| |
| void bc7e_compress_block_params_init_slow(bc7e_compress_block_params * p, bool perceptual) |
| { |
| bc7e_compress_block_params_init(p, perceptual); |
| |
| p->m_alpha_settings.m_max_mode7_partitions_to_try = 2; |
| |
| p->m_pbit_search = true; |
| p->m_uber_level = 0; |
| } |
| |
| void bc7e_compress_block_params_init_basic(bc7e_compress_block_params * p, bool perceptual) |
| { |
| bc7e_compress_block_params_init(p, perceptual); |
| |
| if (perceptual) |
| { |
| p->m_opaque_settings.m_use_mode[0] = false; |
| p->m_opaque_settings.m_use_mode[2] = false; |
| p->m_opaque_settings.m_use_mode[3] = false; |
| p->m_opaque_settings.m_use_mode[4] = false; |
| p->m_opaque_settings.m_use_mode[5] = false; |
| |
| p->m_pbit_search = false; |
| p->m_uber_level = 1; |
| } |
| else |
| { |
| p->m_max_partitions_mode[1] = 32; |
| p->m_max_partitions_mode[2] = 32; |
| p->m_max_partitions_mode[3] = 32; |
| p->m_max_partitions_mode[7] = 32; |
| |
| p->m_opaque_settings.m_use_mode[2] = false; |
| |
| p->m_pbit_search = false; |
| p->m_uber_level = 1; |
| } |
| } |
| |
| void bc7e_compress_block_params_init_fast(bc7e_compress_block_params * p, bool perceptual) |
| { |
| bc7e_compress_block_params_init(p, perceptual); |
| |
| if (perceptual) |
| { |
| p->m_opaque_settings.m_use_mode[0] = false; |
| p->m_opaque_settings.m_use_mode[2] = false; |
| p->m_opaque_settings.m_use_mode[3] = false; |
| p->m_opaque_settings.m_use_mode[4] = false; |
| p->m_opaque_settings.m_use_mode[5] = false; |
| |
| p->m_alpha_settings.m_use_mode5 = false; |
| |
| p->m_opaque_settings.m_max_mode13_partitions_to_try = 1; |
| |
| p->m_pbit_search = false; |
| p->m_uber_level = 0; |
| } |
| else |
| { |
| p->m_opaque_settings.m_use_mode[0] = false; |
| p->m_opaque_settings.m_use_mode[2] = false; |
| p->m_opaque_settings.m_use_mode[4] = false; |
| p->m_opaque_settings.m_use_mode[5] = false; |
| |
| p->m_alpha_settings.m_use_mode5 = false; |
| |
| p->m_opaque_settings.m_max_mode13_partitions_to_try = 2; |
| |
| p->m_pbit_search = false; |
| p->m_uber_level = 0; |
| } |
| } |
| |
| void bc7e_compress_block_params_init_veryfast(bc7e_compress_block_params * p, bool perceptual) |
| { |
| bc7e_compress_block_params_init(p, perceptual); |
| |
| if (perceptual) |
| { |
| p->m_opaque_settings.m_use_mode[0] = false; |
| p->m_opaque_settings.m_use_mode[2] = false; |
| p->m_opaque_settings.m_use_mode[3] = false; |
| p->m_opaque_settings.m_use_mode[4] = false; |
| p->m_opaque_settings.m_use_mode[5] = false; |
| |
| p->m_alpha_settings.m_use_mode5 = false; |
| |
| p->m_pbit_search = false; |
| p->m_uber_level = 0; |
| } |
| else |
| { |
| p->m_opaque_settings.m_use_mode[2] = false; |
| p->m_opaque_settings.m_use_mode[4] = false; |
| p->m_opaque_settings.m_use_mode[5] = false; |
| |
| p->m_alpha_settings.m_use_mode5 = false; |
| |
| p->m_pbit_search = false; |
| p->m_uber_level = 0; |
| } |
| } |
| |
| void bc7e_compress_block_params_init_ultrafast(bc7e_compress_block_params * p, bool perceptual) |
| { |
| bc7e_compress_block_params_init(p, perceptual); |
| |
| p->m_mode6_only = true; |
| |
| p->m_alpha_settings.m_use_mode4 = true; |
| p->m_alpha_settings.m_use_mode5 = true; |
| p->m_alpha_settings.m_use_mode7 = false; |
| |
| p->m_mode4_rotation_mask = 1+4; |
| p->m_mode4_index_mask = 3; |
| p->m_mode5_rotation_mask = 1; |
| |
| p->m_pbit_search = false; |
| p->m_uber_level = 0; |
| } |
| |
| } // namespace bc7e_scalar |