This content was automatically converted from the project's wiki Markdown to HTML. See the Basis Universal GitHub wiki for the latest content.
Copyright (C) 2025-2026 Binomial LLC. All rights reserved except as granted under the Apache 2.0 LICENSE. If you modify the Basis Universal source code, specifications, or wiki documents and redistribute the files, you must cause any modified files to carry prominent notices stating that you changed the files (see Apache 2.0 §4(b)).
This document describes an essential and intricate function used
during XUASTC LDR transcoding,
convert_endpoints_across_cems(), and the helper functions
it calls, and includes a pseudo-code implementation of each.
This key function allows the encoder to efficiently DPCM predict ASTC CEM encoded block endpoints across CEM domains with different BISE endpoint levels.
Note this is actually a small but essential piece of Binomial's analytical ASTC encoder.
XUASTC LDR supports several ASTC LDR endpoint encodings (“CEM’s”), including direct RGB/RGBA, luma/luma-alpha, base+offset, and base+scale. RAW blocks may change the CEM of a block relative to their spatial neighbors, and endpoint DPCM prediction may reference BISE (Bounded Integer Sequence Encoding, or ISE) encoded endpoint values stored using a different CEM than the one required for the current block. For these reasons, endpoint values must sometimes be converted from one ASTC CEM domain to another.
convert_endpoints_across_cems() is responsible for
performing this conversion. It takes an BISE encoded endpoint value set
expressed in a source CEM and source endpoint ISE range, and produces an
equivalent endpoint value set expressed in a destination CEM and
destination endpoint ISE range. The conversion is a purely integer,
deterministic process; it must always exactly reproduce the
reference decoder’s behavior (or decoding will result in a corrupted
output, perhaps subtly).
Crucially, this function MUST be fully deterministic across all platforms, compiler optimization or code generation settings, etc. In practice this means fully integer math must be used.
XUASTC LDR relies on a number of helper routines and lookup tables defined either explicitly by the ASTC specification, or directly derived from it in a relatively straightforward or mechanical way. These routines are provided through the astc_helpers namespace in the reference implementation, but any decoder may implement them independently as long as the results are bit-exact and follow the ASTC LDR specification. These external routines are normative dependencies for the XUASTC LDR format. A conforming XUASTC LDR decoder must provide functions with equivalent mathematical behavior.
The basist::astc_6x6_hdr::g_quantize_tables_preserve2
tables, referred to below, are described in the UASTC
HDR 6x6 Specification. They aid in re-quantizing BISE values from a
source to a target BISE range while preserving key MSB's, and are
commonly used by ASTC encoders.
This document provides a commented pseudo-code
version of the convert_endpoints_across_cems()
function and the key functions it calls. All low-level ASTC-specific
behavior (CEM definitions, endpoint decoding, ISE quantization, Blue
Contraction internals, etc.) is delegated to
astc_helpers.
blue_contract_enc()
— Pseudo-Codestruct color_rgba
{
uint8_t r, g, b, a;
};
color_rgba blue_contract_enc(color_rgba orig, bool& did_clamp, int encoded_b)
{
color_rgba enc;
// Blue contraction adjusts R and G using:
// R' = 2*R - B_enc
// G' = 2*G - B_enc
// This may exceed [0,255], so clamping may be required.
int tr = orig.r * 2 - encoded_b;
int tg = orig.g * 2 - encoded_b;
// If values exceed 8-bit bounds, mark that a clamp occurred.
if ((tr < 0) || (tr > 255) || (tg < 0) || (tg > 255))
did_clamp = true;
// Clamp R,G into valid 8-bit range.
enc.r = (uint8_t)clamp<int>(tr, 0, 255);
enc.g = (uint8_t)clamp<int>(tg, 0, 255);
// Blue and alpha are passed through unchanged.
enc.b = (uint8_t)orig.b;
enc.a = orig.a;
return enc;
}quant_preserve2() —
Pseudo-Codestatic inline int quant_preserve2(uint32_t ise_range, uint32_t v)
{
// If the destination range is 256-level, the value is already correct.
if (ise_range == astc_helpers::BISE_256_LEVELS)
return v;
// All valid endpoint quant ranges are >= BISE_6_LEVELS.
assert(ise_range >= astc_helpers::BISE_6_LEVELS);
// Use the precomputed table that preserves the top two MSBs
// when requantizing base+offset deltas. Refer to the UASTC HDR 6x6 spec for table details.
return basist::astc_6x6_hdr::g_quantize_tables_preserve2[ise_range][v];
}requantize_ise_endpoints()
— Pseudo-Code
static std::vector<uint8_t> g_base_ofs_nudges[astc_helpers::BISE_256_LEVELS+1][2];
//------------------------------------------------------------------------------------------------------
// compute_base_ofs_requantize_tabs(): Computes the g_base_ofs_nudges[] table, used by
// requantize_ise_endpoints()
// -----------------------------------------------------------------------------------------------------
void compute_base_ofs_requantize_tabs()
{
for (uint32_t e_ise_range = astc_helpers::FIRST_VALID_ENDPOINT_ISE_RANGE; e_ise_range <= astc_helpers::LAST_VALID_ENDPOINT_ISE_RANGE; e_ise_range++)
{
const uint32_t num_levels = astc_helpers::get_ise_levels(e_ise_range);
for (uint32_t pos_or_neg = 0; pos_or_neg < 2; pos_or_neg++)
{
g_base_ofs_nudges[e_ise_range][pos_or_neg].resize(num_levels);
const int delta = pos_or_neg ? -1 : 1;
for (uint32_t cur_ise = 0; cur_ise < num_levels; cur_ise++)
{
int cur_dequant = astc_helpers::g_dequant_tables.get_endpoint_tab(e_ise_range).m_ISE_to_val[cur_ise];
int cur_a = cur_dequant, cur_b = 0;
astc_helpers::bit_transfer_signed_dec(cur_a, cur_b);
int best_err = INT_MAX;
uint32_t best_trial_ise = 0;
for (uint32_t trial_ise = 0; trial_ise < num_levels; trial_ise++)
{
int trial_dequant = astc_helpers::g_dequant_tables.get_endpoint_tab(e_ise_range).m_ISE_to_val[trial_ise];
int trial_a = trial_dequant, trial_b = 0;
astc_helpers::bit_transfer_signed_dec(trial_a, trial_b);
// ensure the transferred bit hasn't changed
if (cur_b != trial_b)
continue;
// skip if the decoded delta hasn't changed at all
if (trial_a == cur_a)
continue;
// do they want to nudge neg or pos
if (delta < 0)
{
// neg nudge, but trial delta is higher
if (trial_a > cur_a)
continue;
}
else
{
// pos nudge, but trial delta is lower
if (trial_a < cur_a)
continue;
}
int e = basisu::iabs(trial_a - cur_a);
if (e < best_err)
{
best_err = e;
best_trial_ise = trial_ise;
}
} // trial_ise
if (best_err == INT_MAX)
{
// Failed to nudge, leave it unchanged
best_trial_ise = cur_ise;
}
g_base_ofs_nudges[e_ise_range][pos_or_neg][cur_ise] = (uint8_t)best_trial_ise;
} // cur_ise
} // pos_or_neg
} // e_ise_range
}
//------------------------------------------------------------------------------------------------------
// requantize_ise_endpoints()
// -----------------------------------------------------------------------------------------------------
// Requantizes an endpoint array from one ASTC ISE range to another, while:
// * preserving Blue Contraction state whenever possible
// * preserving MSB structure for base+offset CEMs
// * No floating point computation
//------------------------------------------------------------------------------------------------------
bool requantize_ise_endpoints(
uint32_t cem,
uint32_t src_ise_endpoint_range, // source endpoint quantization range (ISE bucket count)
const uint8_t* pSrc_endpoints, // source endpoint array
uint32_t dst_ise_endpoint_range, // destination endpoint quantization range
uint8_t* pDst_endpoints) // output endpoint array
{
//----------------------------------------------------------------------------------------------
// 0. Validate inputs
//----------------------------------------------------------------------------------------------
if (!astc_helpers::is_cem_ldr(cem))
{
// Should never occur — caller ensures only valid LDR CEMs used
assert(0);
return false;
}
// Get number of endpoint values based on CEM definition.
const uint32_t num_endpoint_vals = astc_helpers::get_num_cem_values(cem);
assert(num_endpoint_vals <= astc_helpers::MAX_CEM_ENDPOINT_VALS);
//----------------------------------------------------------------------------------------------
// 1. If source and destination quantization ranges match, we're done
//----------------------------------------------------------------------------------------------
if (src_ise_endpoint_range == dst_ise_endpoint_range)
{
// byte-for-byte copy, no transform needed
memcpy(pDst_endpoints, pSrc_endpoints, num_endpoint_vals);
return true;
}
//----------------------------------------------------------------------------------------------
// 2. Convert source endpoints → canonical 8-bit domain (0–255)
//----------------------------------------------------------------------------------------------
uint8_t dequantized_src_vals_temp[astc_helpers::MAX_CEM_ENDPOINT_VALS];
const uint8_t* pDequantized_src_vals = pSrc_endpoints;
if (src_ise_endpoint_range != astc_helpers::BISE_256_LEVELS)
{
// Load dequantization table for the source ISE range.
const auto& dequant_tab =
astc_helpers::g_dequant_tables
.get_endpoint_tab(src_ise_endpoint_range)
.m_ISE_to_val;
// Convert each endpoint from ISE code → 8-bit canonical
for (uint32_t i = 0; i < num_endpoint_vals; i++)
dequantized_src_vals_temp[i] = dequant_tab[pSrc_endpoints[i]];
pDequantized_src_vals = dequantized_src_vals_temp;
}
//----------------------------------------------------------------------------------------------
// 3. Destination = 256-level (raw bytes). Just output canonical values
//----------------------------------------------------------------------------------------------
if (dst_ise_endpoint_range == astc_helpers::BISE_256_LEVELS)
{
// Already in 8-bit canonical form → direct copy
memcpy(pDst_endpoints, pDequantized_src_vals, num_endpoint_vals);
return true;
}
//----------------------------------------------------------------------------------------------
// 4. Load destination quant table for final remapping
//----------------------------------------------------------------------------------------------
const auto& dst_quant_tab =
astc_helpers::g_dequant_tables
.get_endpoint_tab(dst_ise_endpoint_range)
.m_val_to_ise;
//==============================================================================================
// SPECIAL CASE A — BASE+OFFSET CEMs
//==============================================================================================
if ((cem == astc_helpers::CEM_LDR_RGB_BASE_PLUS_OFFSET) ||
(cem == astc_helpers::CEM_LDR_RGBA_BASE_PLUS_OFFSET))
{
// Load destination dequant table (ISE code → 8-bit)
const auto& dst_dequant_tab =
astc_helpers::g_dequant_tables
.get_endpoint_tab(dst_ise_endpoint_range)
.m_ISE_to_val;
//------------------------------------------------------------------------------------------
// A1. First pass: Quantize bases normally, deltas with MSB-preserving transform
//------------------------------------------------------------------------------------------
for (uint32_t i = 0; i < num_endpoint_vals; i++)
{
if (i & 1)
{
// Odd index → delta value → preserve top 2 MSBs
pDst_endpoints[i] =
(uint8_t)quant_preserve2(dst_ise_endpoint_range,
pDequantized_src_vals[i]);
}
else
{
// Even index → base value → normal quantization
pDst_endpoints[i] =
dst_quant_tab[pDequantized_src_vals[i]];
}
}
#if defined(_DEBUG) || defined(DEBUG)
{
//--------------------------------------------------------------------------------------
// Debug check: Ensure MSB invariants preserved
//--------------------------------------------------------------------------------------
const auto& src_dequant_tab =
astc_helpers::g_dequant_tables
.get_endpoint_tab(src_ise_endpoint_range)
.m_ISE_to_val;
for (uint32_t i = 0; i < num_endpoint_vals; i++)
{
int src_v = src_dequant_tab[pSrc_endpoints[i]];
int dst_v = dst_dequant_tab[pDst_endpoints[i]];
// MSB (bit7) must match in ALL endpoints
assert((src_v & 128) == (dst_v & 128));
// For delta endpoints, preserve next MSB (bit6)
if (i & 1)
assert((src_v & 64) == (dst_v & 64));
}
}
#endif
//------------------------------------------------------------------------------------------
// A2. Detect if source endpoints used Blue Contraction
//------------------------------------------------------------------------------------------
const bool src_used_blue_contract =
astc_helpers::used_blue_contraction(
cem, pSrc_endpoints, src_ise_endpoint_range);
//------------------------------------------------------------------------------------------
// A3. Compute BC state after quantization
//------------------------------------------------------------------------------------------
int v0 = dst_dequant_tab[pDst_endpoints[0]];
int v1 = dst_dequant_tab[pDst_endpoints[1]];
int v2 = dst_dequant_tab[pDst_endpoints[2]];
int v3 = dst_dequant_tab[pDst_endpoints[3]];
int v4 = dst_dequant_tab[pDst_endpoints[4]];
int v5 = dst_dequant_tab[pDst_endpoints[5]];
// Undo bit-transfer encoding (ASTC base+offset delta decode)
astc_helpers::bit_transfer_signed_dec(v1, v0);
astc_helpers::bit_transfer_signed_dec(v3, v2);
astc_helpers::bit_transfer_signed_dec(v5, v4);
// Blue Contraction detection → negative delta sum
int s = v1 + v3 + v5;
bool quant_used_blue_contraction = (s < 0);
//------------------------------------------------------------------------------------------
// A4. If BC state changed → attempt up to 5 nudges using precomputed table
//------------------------------------------------------------------------------------------
const uint32_t MAX_TRIES = 5;
uint32_t tries = 0;
if (src_used_blue_contract != quant_used_blue_contraction)
{
// Determine needed direction: +1 or -1 adjustments.
int nudge_delta = quant_used_blue_contraction ? 1 : -1;
// Start nudging from blue component delta by default
uint32_t cur_c_rover = 2;
for (tries = 0; tries < MAX_TRIES; tries++)
{
for (uint32_t j = 0; j < 3; j++)
{
const uint32_t c = (cur_c_rover + j) % 3;
const uint32_t idx = 1 + c * 2; // delta index
// Attempt MSB-safe nudge
uint32_t new_ise_v =
g_base_ofs_nudges
[dst_ise_endpoint_range]
[(nudge_delta < 0) ? 1 : 0]
[pDst_endpoints[idx]];
if (new_ise_v != pDst_endpoints[idx])
{
// Successful nudge
pDst_endpoints[idx] = (uint8_t)new_ise_v;
break;
}
}
// Recompute BC state after nudge
v0 = dst_dequant_tab[pDst_endpoints[0]];
v1 = dst_dequant_tab[pDst_endpoints[1]];
v2 = dst_dequant_tab[pDst_endpoints[2]];
v3 = dst_dequant_tab[pDst_endpoints[3]];
v4 = dst_dequant_tab[pDst_endpoints[4]];
v5 = dst_dequant_tab[pDst_endpoints[5]];
astc_helpers::bit_transfer_signed_dec(v1, v0);
astc_helpers::bit_transfer_signed_dec(v3, v2);
astc_helpers::bit_transfer_signed_dec(v5, v4);
s = v1 + v3 + v5;
quant_used_blue_contraction = (s < 0);
if (src_used_blue_contract == quant_used_blue_contraction)
break; // BC successfully restored
++cur_c_rover; // try next component
}
}
#if defined(_DEBUG) || defined(DEBUG)
if (tries < MAX_TRIES)
{
// Verify BC state fully preserved
assert(
astc_helpers::used_blue_contraction(
cem, pDst_endpoints, dst_ise_endpoint_range)
==
astc_helpers::used_blue_contraction(
cem, pSrc_endpoints, src_ise_endpoint_range));
}
#endif
return true;
}
//==============================================================================================
// SPECIAL CASE B — DIRECT RGB/RGBA CEMs
//==============================================================================================
else if ((cem == astc_helpers::CEM_LDR_RGB_DIRECT) ||
(cem == astc_helpers::CEM_LDR_RGBA_DIRECT))
{
const auto& dst_dequant_tab =
astc_helpers::g_dequant_tables
.get_endpoint_tab(dst_ise_endpoint_range)
.m_ISE_to_val;
//------------------------------------------------------------------------------------------
// B1. Detect original BC usage via canonical sums
//------------------------------------------------------------------------------------------
uint32_t s0 =
pDequantized_src_vals[0] +
pDequantized_src_vals[2] +
pDequantized_src_vals[4];
uint32_t s1 =
pDequantized_src_vals[1] +
pDequantized_src_vals[3] +
pDequantized_src_vals[5];
const bool orig_used_blue_contract = (s1 < s0);
//------------------------------------------------------------------------------------------
// B2. Quantize normally using destination quant table
//------------------------------------------------------------------------------------------
for (uint32_t i = 0; i < num_endpoint_vals; i++)
pDst_endpoints[i] = dst_quant_tab[pDequantized_src_vals[i]];
//------------------------------------------------------------------------------------------
// B3. Compute BC state after quantization
//------------------------------------------------------------------------------------------
uint32_t dequant_s0 =
dst_dequant_tab[pDst_endpoints[0]] +
dst_dequant_tab[pDst_endpoints[2]] +
dst_dequant_tab[pDst_endpoints[4]];
uint32_t dequant_s1 =
dst_dequant_tab[pDst_endpoints[1]] +
dst_dequant_tab[pDst_endpoints[3]] +
dst_dequant_tab[pDst_endpoints[5]];
const bool quant_used_blue_contract =
(dequant_s1 < dequant_s0);
//------------------------------------------------------------------------------------------
// B4. If BC flipped, fix it
//------------------------------------------------------------------------------------------
if (orig_used_blue_contract != quant_used_blue_contract)
{
if (dequant_s0 == dequant_s1)
{
// Case: sums equal → must *nudge* to enforce s1 < s0
assert(orig_used_blue_contract);
assert(!quant_used_blue_contract);
if (dequant_s1)
{
// Reduce s1 by nudging high endpoints
for (uint32_t i = 0; i < 3; i++)
{
uint32_t new_ise =
astc_helpers::apply_delta_to_bise_endpoint_val(
dst_ise_endpoint_range,
pDst_endpoints[1 + i*2],
-1);
if (new_ise != pDst_endpoints[1 + i*2])
{
pDst_endpoints[1 + i*2] = (uint8_t)new_ise;
break;
}
}
}
else
{
// Both are 0 → increase s0
for (uint32_t i = 0; i < 3; i++)
{
uint32_t new_ise =
astc_helpers::apply_delta_to_bise_endpoint_val(
dst_ise_endpoint_range,
pDst_endpoints[i*2],
1);
if (new_ise != pDst_endpoints[i*2])
{
pDst_endpoints[i*2] = (uint8_t)new_ise;
break;
}
}
}
}
else
{
// Case: sums differ → simply swap L/H order
std::swap(pDst_endpoints[0], pDst_endpoints[1]);
std::swap(pDst_endpoints[2], pDst_endpoints[3]);
std::swap(pDst_endpoints[4], pDst_endpoints[5]);
if (cem == astc_helpers::CEM_LDR_RGBA_DIRECT)
std::swap(pDst_endpoints[6], pDst_endpoints[7]);
}
}
// Ensure BC state matches after adjustment
assert(
astc_helpers::used_blue_contraction(
cem, pDst_endpoints, dst_ise_endpoint_range)
==
astc_helpers::used_blue_contraction(
cem, pSrc_endpoints, src_ise_endpoint_range));
return true;
}
//==============================================================================================
// SPECIAL CASE C — All Other CEMs
//==============================================================================================
else
{
// Simple quantization (no MSB constraints)
for (uint32_t i = 0; i < num_endpoint_vals; i++)
pDst_endpoints[i] =
dst_quant_tab[pDequantized_src_vals[i]];
#if defined(_DEBUG) || defined(DEBUG)
{
// Verify MSB preservation for CEM types that require it
const auto& src_dequant_tab =
astc_helpers::g_dequant_tables
.get_endpoint_tab(src_ise_endpoint_range)
.m_ISE_to_val;
const auto& dst_dequant_tab =
astc_helpers::g_dequant_tables
.get_endpoint_tab(dst_ise_endpoint_range)
.m_ISE_to_val;
for (uint32_t i = 0; i < num_endpoint_vals; i++)
{
int src_v = src_dequant_tab[pSrc_endpoints[i]];
int dst_v = dst_dequant_tab[pDst_endpoints[i]];
// MSB must match
assert((src_v & 128) == (dst_v & 128));
}
}
#endif
return true;
}
}pack_base_offset() —
Pseudo-Code//--------------------------------------------------------------------------------------------------
// pack_base_offset()
//
// Packs (L, H) endpoints into ASTC Base+Offset form:
// * First packs into full 8-bit domain (ISE_256)
// * Enforces blue contraction if requested
// * Computes deltas (dr,dg,db,da) clamped to [-32,31]
// * Attempts multiple swap/clamp passes to ensure correct BC sign
// * Encodes with ASTC bit_transfer_signed_enc()
// * Then requantizes down to dst_ise_endpoint_range
// * No floating point
//
//--------------------------------------------------------------------------------------------------
bool pack_base_offset(
uint32_t cem_index, // Must be RGB_BASE_PLUS_OFFSET or RGBA_BASE_PLUS_OFFSET
uint32_t dst_ise_endpoint_range, // Destination endpoint quantization range
uint8_t* pPacked_endpoints, // Output final packed endpoints (ISE domain)
const color_rgba& l, // canonical 8-bit low endpoint
const color_rgba& h, // canonical 8-bit high endpoint
bool use_blue_contraction, // Whether BC must be enforced
bool auto_disable_blue_contraction_if_clamped,
bool& blue_contraction_clamped_flag, // Output: BC clamped occurred
bool& base_ofs_clamped_flag, // Output: delta clamp occurred
bool& endpoints_swapped) // Output: whether L/H were swapped
{
blue_contraction_clamped_flag = false;
base_ofs_clamped_flag = false;
endpoints_swapped = false;
// Validate CEM is allowed.
if ((cem_index != astc_helpers::CEM_LDR_RGB_BASE_PLUS_OFFSET) &&
(cem_index != astc_helpers::CEM_LDR_RGBA_BASE_PLUS_OFFSET))
{
assert(0);
return false;
}
// Local working copies of L/H — may be swapped
color_rgba pack_l(l);
color_rgba pack_h(h);
//----------------------------------------------------------------------------------------------
// STEP 1 — Apply Blue Contraction if requested
//----------------------------------------------------------------------------------------------
if (use_blue_contraction)
{
// Encode both endpoints through BC (integer transform)
color_rgba enc_l(blue_contract_enc(pack_l, blue_contraction_clamped_flag, pack_l.b));
color_rgba enc_h(blue_contract_enc(pack_h, blue_contraction_clamped_flag, pack_h.b));
if (blue_contraction_clamped_flag && auto_disable_blue_contraction_if_clamped)
{
// BC was clamped → automatically disable BC entirely
use_blue_contraction = false;
}
else
{
// Swap endpoints for BC encoding (ASTC rule)
pack_h = enc_l;
pack_l = enc_h;
endpoints_swapped = true;
}
}
//----------------------------------------------------------------------------------------------
// STEP 2 — Compute deltas with integer clamping
//----------------------------------------------------------------------------------------------
int dr = 0, dg = 0, db = 0, da = 0;
bool pack_uses_blue_contraction = false;
int low_clamp = -32; // First two passes clamp deltas to [-32,31]
// Later passes adjust this (see below)
// Up to 4 passes:
// Pass 0–1: asymmetric clamp range low_clamp = -32
// Pass 2–3: symmetric clamp range low_clamp = -31
// Swaps L/H between passes until BC sign is correct
for (uint32_t pass = 0; pass < 4; pass++)
{
// Compute “original” deltas
int orig_dr = pack_h.r - pack_l.r;
int orig_dg = pack_h.g - pack_l.g;
int orig_db = pack_h.b - pack_l.b;
int orig_da = pack_h.a - pack_l.a;
base_ofs_clamped_flag = false;
// Clamp each delta to [-32,31] (or later [-31,31])
dr = clamp<int>(orig_dr, low_clamp, 31);
dg = clamp<int>(orig_dg, low_clamp, 31);
db = clamp<int>(orig_db, low_clamp, 31);
da = clamp<int>(orig_da, low_clamp, 31);
if (dr != orig_dr) base_ofs_clamped_flag = true;
if (dg != orig_dg) base_ofs_clamped_flag = true;
if (db != orig_db) base_ofs_clamped_flag = true;
if (da != orig_da) base_ofs_clamped_flag = true;
// Compute sum of RGB deltas → sign determines blue contraction usage
int s = dr + dg + db;
pack_uses_blue_contraction = (s < 0);
// If delta sum sign matches required BC usage, we are DONE.
if (pack_uses_blue_contraction == use_blue_contraction)
break;
//------------------------------------------------------------------------------------------
// Special case: if sum==0 and BC required → force a negative sum manually
//------------------------------------------------------------------------------------------
if (s == 0)
{
assert(!pack_uses_blue_contraction);
assert(use_blue_contraction);
// Force sum negative: decrement db if possible,
// otherwise dr or dg. Guaranteed possible for valid endpoints.
if (db > -32)
db--;
else if (dr > -32)
dr--;
else if (dg > -32)
dg--;
else
assert(0); // unreachable
assert((dr + dg + db) < 0);
pack_uses_blue_contraction = true;
break;
}
//------------------------------------------------------------------------------------------
// If this is the last pass, failure should be impossible
//------------------------------------------------------------------------------------------
if (pass == 3)
{
// Theoretically unreachable due to ASTC constraints
assert(0);
break;
}
//------------------------------------------------------------------------------------------
// After 2 passes, use symmetric clamp range [-31,31] instead of [-32,31]
//------------------------------------------------------------------------------------------
if (pass == 1)
{
low_clamp = -31;
}
// Swap endpoints and continue searching for a valid BC-compatible pair
std::swap(pack_l, pack_h);
endpoints_swapped = !endpoints_swapped;
} // end for pass
//----------------------------------------------------------------------------------------------
// STEP 3 — Encode Base+Offset using ASTC bit_transfer_signed_enc()
// (This moves MSBs into the “base” endpoints to preserve sign during ISE)
//----------------------------------------------------------------------------------------------
int v0 = pack_l.r, v2 = pack_l.g, v4 = pack_l.b;
int v1 = dr, v3 = dg, v5 = db;
// Encode L/H deltas into ASTC Base+Offset form (lossless at 8 bits)
astc_helpers::bit_transfer_signed_enc(v1, v0);
astc_helpers::bit_transfer_signed_enc(v3, v2);
astc_helpers::bit_transfer_signed_enc(v5, v4);
// Optional alpha channel support
int v6 = 0, v7 = 0;
if (astc_helpers::does_cem_have_alpha(cem_index))
{
v6 = pack_l.a;
v7 = da;
astc_helpers::bit_transfer_signed_enc(v7, v6);
}
//----------------------------------------------------------------------------------------------
// STEP 4 — Store into temporary 8-bit endpoint array
//----------------------------------------------------------------------------------------------
uint8_t new_endpoints8[astc_helpers::MAX_CEM_ENDPOINT_VALS];
new_endpoints8[0] = (uint8_t)v0;
new_endpoints8[1] = (uint8_t)v1;
new_endpoints8[2] = (uint8_t)v2;
new_endpoints8[3] = (uint8_t)v3;
new_endpoints8[4] = (uint8_t)v4;
new_endpoints8[5] = (uint8_t)v5;
if (astc_helpers::does_cem_have_alpha(cem_index))
{
new_endpoints8[6] = (uint8_t)v6;
new_endpoints8[7] = (uint8_t)v7;
}
// ASTC invariant: BC usage must match after packing at 8-bit precision
assert(
astc_helpers::used_blue_contraction(
cem_index, new_endpoints8, astc_helpers::BISE_256_LEVELS)
==
use_blue_contraction);
//----------------------------------------------------------------------------------------------
// STEP 5 — Requantize final 8-bit endpoints down to the destination ISE range
//----------------------------------------------------------------------------------------------
bool status = requantize_ise_endpoints(
cem_index,
astc_helpers::BISE_256_LEVELS, // source = 8-bit domain
new_endpoints8, // 8-bit Base+Offset array
dst_ise_endpoint_range, // final destination quant range
pPacked_endpoints); // output
// Can't assert BC preservation here, because extremely-low-ISE quantization
// could theoretically fail to preserve BC. It's rare, but possible.
return status;
}convert_endpoints_across_cems()
— Pseudo-Codebool convert_endpoints_across_cems(
uint32_t prev_cem,
uint32_t prev_endpoint_ise_range,
const uint8_t* pPrev_endpoints,
uint32_t dst_cem,
uint32_t dst_endpoint_ise_range,
uint8_t* pDst_endpoints,
bool always_repack,
bool use_blue_contraction,
bool auto_disable_blue_contraction_if_clamped,
bool &blue_contraction_clamped_flag,
bool &base_ofs_clamped_flag)
{
// Reset output flags
blue_contraction_clamped_flag = false;
base_ofs_clamped_flag = false;
// Number of endpoint values for the destination CEM
const uint32_t num_dst_endpoint_vals =
astc_helpers::get_num_cem_values(dst_cem);
// Destination quantization/dequantization tables
const auto &dst_quant_tab =
astc_helpers::g_dequant_tables.get_endpoint_tab(dst_endpoint_ise_range).m_val_to_ise;
const auto &dst_dequant_tab =
astc_helpers::g_dequant_tables.get_endpoint_tab(dst_endpoint_ise_range).m_ISE_to_val;
//----------------------------------------------------------------------
// 1. Fast path: CEMs exactly equal and not forced to repack
//----------------------------------------------------------------------
if ((prev_cem == dst_cem) && (!always_repack))
{
// Requantize previous block's endpoints into the current block's
// endpoint ISE range and return.
return requantize_ise_endpoints(
prev_cem, prev_endpoint_ise_range, pPrev_endpoints,
dst_endpoint_ise_range, pDst_endpoints);
}
//----------------------------------------------------------------------
// 2. Fast path: Same "base" CEM ignoring alpha, and not always_repack
//----------------------------------------------------------------------
if (!always_repack)
{
const uint32_t prev_base_cem =
astc_helpers::get_base_cem_without_alpha(prev_cem);
const uint32_t dst_base_cem =
astc_helpers::get_base_cem_without_alpha(dst_cem);
// Case A: prev CEM has alpha, dst CEM does not, base CEMs match.
// We can strip alpha by requantizing as the base CEM.
if ((prev_base_cem == dst_base_cem) &&
(!astc_helpers::does_cem_have_alpha(dst_cem)))
{
// Sanity checks:
// - prev CEM must have alpha
// - number of values in the base CEM must match dst CEM's count
assert(astc_helpers::does_cem_have_alpha(prev_cem));
assert(astc_helpers::get_num_cem_values(prev_base_cem) ==
num_dst_endpoint_vals);
return requantize_ise_endpoints(
prev_base_cem, prev_endpoint_ise_range, pPrev_endpoints,
dst_endpoint_ise_range, pDst_endpoints);
}
// Case B: prev CEM has no alpha, dst CEM does, base CEMs match.
// We add "sane" alpha values (255) after requantization.
if ((prev_base_cem == dst_base_cem) &&
astc_helpers::does_cem_have_alpha(dst_cem))
{
assert(!astc_helpers::does_cem_have_alpha(prev_base_cem));
// Requantize previous endpoints into the dst endpoint ISE range
// using the base CEM (no alpha).
bool status = requantize_ise_endpoints(
prev_base_cem, prev_endpoint_ise_range, pPrev_endpoints,
dst_endpoint_ise_range, pDst_endpoints);
if (!status)
return false;
// Plug in 255 for alpha (as reasonable defaults).
const int ise_a_val = dst_quant_tab[255];
switch (dst_cem)
{
case astc_helpers::CEM_LDR_LUM_ALPHA_DIRECT:
{
assert(num_dst_endpoint_vals == 4);
pDst_endpoints[2] = (uint8_t)ise_a_val;
pDst_endpoints[3] = (uint8_t)ise_a_val;
break;
}
case astc_helpers::CEM_LDR_RGBA_DIRECT:
{
assert(num_dst_endpoint_vals == 8);
pDst_endpoints[6] = (uint8_t)ise_a_val;
pDst_endpoints[7] = (uint8_t)ise_a_val;
break;
}
case astc_helpers::CEM_LDR_RGB_BASE_SCALE_PLUS_TWO_A:
{
assert(num_dst_endpoint_vals == 6);
pDst_endpoints[4] = (uint8_t)ise_a_val;
pDst_endpoints[5] = (uint8_t)ise_a_val;
break;
}
case astc_helpers::CEM_LDR_RGBA_BASE_PLUS_OFFSET:
{
assert(num_dst_endpoint_vals == 8);
// Alphas should decode to 255,255.
pDst_endpoints[6] = (uint8_t)ise_a_val; // base alpha
pDst_endpoints[7] = (uint8_t)dst_quant_tab[128]; // offset alpha
break;
}
default:
assert(0);
break;
}
return true;
}
} // !always_repack
//----------------------------------------------------------------------
// 3. General path: CEMs are not in the same simple class
//----------------------------------------------------------------------
//
// Decode endpoints to canonical RGBA low/high, then repack into dst CEM.
//
color_rgba prev_l, prev_h;
decode_endpoints(prev_cem, pPrev_endpoints, prev_endpoint_ise_range,
prev_l, prev_h);
// Temporary 8-bit storage for endpoints in the destination CEM layout
uint8_t new_endpoints8[astc_helpers::MAX_CEM_ENDPOINT_VALS] = { 0 };
// Pack canonical endpoints into the desired CEM in 8-bit space.
switch (dst_cem)
{
//------------------------------------------------------------------
// LDR LUMA / LUMA+ALPHA
//------------------------------------------------------------------
case astc_helpers::CEM_LDR_LUM_DIRECT:
case astc_helpers::CEM_LDR_LUM_ALPHA_DIRECT:
{
// Convert to luma/alpha low/high.
new_endpoints8[0] =
(uint8_t)((prev_l.r + prev_l.g + prev_l.b + 1) / 3);
new_endpoints8[1] =
(uint8_t)((prev_h.r + prev_h.g + prev_h.b + 1) / 3);
if (dst_cem == astc_helpers::CEM_LDR_LUM_ALPHA_DIRECT)
{
new_endpoints8[2] = prev_l.a;
new_endpoints8[3] = prev_h.a;
}
// If coming from a non-luma CEM, enforce L < H for better prediction.
if ((prev_cem != astc_helpers::CEM_LDR_LUM_DIRECT) &&
(prev_cem != astc_helpers::CEM_LDR_LUM_ALPHA_DIRECT))
{
if (new_endpoints8[0] > new_endpoints8[1])
{
std::swap(new_endpoints8[0], new_endpoints8[1]);
std::swap(new_endpoints8[2], new_endpoints8[3]);
}
}
// Requantize 256-level endpoints to dst ISE range.
return requantize_ise_endpoints(
dst_cem, astc_helpers::BISE_256_LEVELS, new_endpoints8,
dst_endpoint_ise_range, pDst_endpoints);
}
//------------------------------------------------------------------
// LDR RGB / RGBA DIRECT
//------------------------------------------------------------------
case astc_helpers::CEM_LDR_RGB_DIRECT:
case astc_helpers::CEM_LDR_RGBA_DIRECT:
{
// Convert to RGB(A) direct; preserve original ordering to preserve
// previous Blue Contraction usage (if any).
new_endpoints8[0] = prev_l.r;
new_endpoints8[1] = prev_h.r;
new_endpoints8[2] = prev_l.g;
new_endpoints8[3] = prev_h.g;
new_endpoints8[4] = prev_l.b;
new_endpoints8[5] = prev_h.b;
if (dst_cem == astc_helpers::CEM_LDR_RGBA_DIRECT)
{
new_endpoints8[6] = prev_l.a;
new_endpoints8[7] = prev_h.a;
}
// Optional Blue Contraction usage (if requested).
if (use_blue_contraction)
{
color_rgba enc_l = blue_contract_enc(
prev_l,
blue_contraction_clamped_flag,
dst_dequant_tab[dst_quant_tab[prev_l.b]]);
color_rgba enc_h = blue_contract_enc(
prev_h,
blue_contraction_clamped_flag,
dst_dequant_tab[dst_quant_tab[prev_h.b]]);
if (auto_disable_blue_contraction_if_clamped &&
blue_contraction_clamped_flag)
{
// BC clamped; disable BC for this conversion.
use_blue_contraction = false;
}
else
{
// Swap L/H ordering for BC-encoded endpoints.
new_endpoints8[0] = enc_h.r;
new_endpoints8[1] = enc_l.r;
new_endpoints8[2] = enc_h.g;
new_endpoints8[3] = enc_l.g;
new_endpoints8[4] = enc_h.b;
new_endpoints8[5] = enc_l.b;
if (dst_cem == astc_helpers::CEM_LDR_RGBA_DIRECT)
{
new_endpoints8[6] = prev_h.a;
new_endpoints8[7] = prev_l.a;
}
}
}
// Evaluate BC state by sum comparison (s1 < s0).
uint32_t s0 = new_endpoints8[0] + new_endpoints8[2] + new_endpoints8[4];
uint32_t s1 = new_endpoints8[1] + new_endpoints8[3] + new_endpoints8[5];
bool pack_used_blue_contraction = (s1 < s0);
if (pack_used_blue_contraction != use_blue_contraction)
{
if (s0 == s1)
{
// BC states differ but sums are equal. Force BC usage by
// nudging one component (either lowering s1 or raising s0).
if (s1)
{
// Decrease s1: modify a high endpoint channel slightly.
for (uint32_t i = 0; i < 3; i++)
{
uint32_t new_ise_v =
astc_helpers::apply_delta_to_bise_endpoint_val(
astc_helpers::BISE_256_LEVELS,
new_endpoints8[1 + i * 2], -1);
if (new_ise_v != new_endpoints8[1 + i * 2])
{
new_endpoints8[1 + i * 2] = (uint8_t)new_ise_v;
break;
}
}
}
else
{
// Both sums are 0; increase s0 slightly.
for (uint32_t i = 0; i < 3; i++)
{
uint32_t new_ise_val =
astc_helpers::apply_delta_to_bise_endpoint_val(
astc_helpers::BISE_256_LEVELS,
new_endpoints8[i * 2], 1);
if (new_ise_val != new_endpoints8[i * 2])
{
new_endpoints8[i * 2] = (uint8_t)new_ise_val;
break;
}
}
}
}
else
{
// Just swap all L/H endpoint pairs.
for (uint32_t i = 0; i < num_dst_endpoint_vals; i += 2)
std::swap(new_endpoints8[i], new_endpoints8[i + 1]);
}
}
// Requantize to destination ISE range.
bool status = requantize_ise_endpoints(
dst_cem, astc_helpers::BISE_256_LEVELS, new_endpoints8,
dst_endpoint_ise_range, pDst_endpoints);
if (!status)
return false;
return true;
}
//------------------------------------------------------------------
// LDR RGB BASE+SCALE / BASE+SCALE+TWO_A
//------------------------------------------------------------------
case astc_helpers::CEM_LDR_RGB_BASE_SCALE:
case astc_helpers::CEM_LDR_RGB_BASE_SCALE_PLUS_TWO_A:
{
color_rgba lc(prev_l), hc(prev_h);
// If coming from a non-base-scale CEM, reorder endpoints based on
// brightness to normalize base/scale packing.
if ((prev_cem != astc_helpers::CEM_LDR_RGB_BASE_SCALE) &&
(prev_cem != astc_helpers::CEM_LDR_RGB_BASE_SCALE_PLUS_TWO_A))
{
if ((lc.r + lc.g + lc.b) > (hc.r + hc.g + hc.b))
{
std::swap(lc, hc);
}
}
// Base color is hc (high), scale is derived from LC/HC correlation.
new_endpoints8[0] = hc.r;
new_endpoints8[1] = hc.g;
new_endpoints8[2] = hc.b;
// Integer-only scale approximation
{
int id = (lc.r * hc.r) + (lc.g * hc.g) + (lc.b * hc.b);
int inrm = (hc.r * hc.r) + (hc.g * hc.g) + (hc.b * hc.b);
const int IMAX_S = (1024 * 255) / 256;
int iscale = IMAX_S;
if (inrm > 0)
iscale = (id * 1024) / inrm;
iscale = clamp<int>(iscale, 0, IMAX_S);
iscale = (iscale + 2) >> 2;
iscale = clamp<int>(iscale, 0, 255);
new_endpoints8[3] = static_cast<uint8_t>(iscale);
}
if (dst_cem == astc_helpers::CEM_LDR_RGB_BASE_SCALE_PLUS_TWO_A)
{
new_endpoints8[4] = lc.a;
new_endpoints8[5] = hc.a;
if ((prev_cem != astc_helpers::CEM_LDR_RGB_BASE_SCALE) &&
(prev_cem != astc_helpers::CEM_LDR_RGB_BASE_SCALE_PLUS_TWO_A))
{
// Ensure alpha endpoints ordered for correlated alpha.
if (new_endpoints8[4] > new_endpoints8[5])
std::swap(new_endpoints8[4], new_endpoints8[5]);
}
}
// Requantize from 256-level temporary to destination ISE range.
return requantize_ise_endpoints(
dst_cem, astc_helpers::BISE_256_LEVELS, new_endpoints8,
dst_endpoint_ise_range, pDst_endpoints);
}
//------------------------------------------------------------------
// LDR RGB / RGBA BASE+OFFSET
//------------------------------------------------------------------
case astc_helpers::CEM_LDR_RGB_BASE_PLUS_OFFSET:
case astc_helpers::CEM_LDR_RGBA_BASE_PLUS_OFFSET:
{
bool endpoints_swapped = false;
// Delegate to base+offset packer, which:
// - Picks base/delta in 8-bit space.
// - Enforces ASTC base+offset invariants.
// - Applies Blue Contraction if requested.
// - Sets blue_contraction_clamped_flag and base_ofs_clamped_flag
// when clamping occurs.
return pack_base_offset(
dst_cem,
dst_endpoint_ise_range,
pDst_endpoints,
prev_l,
prev_h,
use_blue_contraction,
auto_disable_blue_contraction_if_clamped,
blue_contraction_clamped_flag,
base_ofs_clamped_flag,
endpoints_swapped);
}
//------------------------------------------------------------------
// Unknown destination CEM
//------------------------------------------------------------------
default:
{
assert(0);
return false;
}
}
// All code paths that fall through the switch must have returned already.
return true;
}