struct ModeInfo {
u8 num_subsets; u8 partition_bits; u8 rotation_bits; u8 idx_sel_bit; u8 color_bits; u8 alpha_bits; u8 ep_pbit; u8 shared_pbit; u8 index_bits; u8 index_bits2; };
namespace {
constexpr std::array<ModeInfo, 8> BC7_MODES = {{
{3, 4, 0, 0, 4, 0, 1, 0, 3, 0},
{2, 6, 0, 0, 6, 0, 0, 1, 3, 0},
{3, 6, 0, 0, 5, 0, 0, 0, 2, 0},
{2, 6, 0, 0, 7, 0, 1, 0, 2, 0},
{1, 0, 2, 1, 5, 6, 0, 0, 2, 3},
{1, 0, 2, 0, 7, 8, 0, 0, 2, 2},
{1, 0, 0, 0, 7, 7, 1, 0, 4, 0},
{2, 6, 0, 0, 5, 5, 1, 0, 2, 0},
}};
void decode_block(const u8* block, u8* out) {
BitReader reader(block);
u32 mode = 0;
while (mode < 8 && reader.read(1) == 0)
++mode;
if (mode >= 8) {
for (u32 i = 0; i < 16; ++i) {
out[i * 4 + 0] = 0;
out[i * 4 + 1] = 0;
out[i * 4 + 2] = 0;
out[i * 4 + 3] = 255;
}
return;
}
const ModeInfo& mode_info = BC7_MODES[mode];
const u32 partition = mode_info.partition_bits ? reader.read(mode_info.partition_bits) : 0;
const u32 rotation = mode_info.rotation_bits ? reader.read(mode_info.rotation_bits) : 0;
const u32 idx_sel = mode_info.idx_sel_bit ? reader.read(1) : 0;
const u32 num_subsets = mode_info.num_subsets;
const u32 num_ep = num_subsets * 2;
std::array<std::array<u32, 4>, 6> endpoints{};
for (u32 ch = 0; ch < 3; ++ch) {
for (u32 ep = 0; ep < num_ep; ++ep)
endpoints[ep][ch] = reader.read(mode_info.color_bits);
}
if (mode_info.alpha_bits) {
for (u32 ep = 0; ep < num_ep; ++ep)
endpoints[ep][3] = reader.read(mode_info.alpha_bits);
}
if (mode_info.ep_pbit) {
std::array<u32, 6> pbits{};
for (u32 ep = 0; ep < num_ep; ++ep)
pbits[ep] = reader.read(1);
for (u32 ep = 0; ep < num_ep; ++ep) {
for (u32 ch = 0; ch < 3; ++ch)
endpoints[ep][ch] = (endpoints[ep][ch] << 1) | pbits[ep];
if (mode_info.alpha_bits)
endpoints[ep][3] = (endpoints[ep][3] << 1) | pbits[ep];
}
const u32 color_prec = mode_info.color_bits + 1;
const u32 alpha_prec = mode_info.alpha_bits ? mode_info.alpha_bits + 1 : 0;
for (u32 ep = 0; ep < num_ep; ++ep) {
for (u32 ch = 0; ch < 3; ++ch)
endpoints[ep][ch] = bc7_unquantize(endpoints[ep][ch], color_prec);
if (mode_info.alpha_bits)
endpoints[ep][3] = bc7_unquantize(endpoints[ep][3], alpha_prec);
else
endpoints[ep][3] = 255;
}
} else if (mode_info.shared_pbit) {
std::array<u32, 3> pbits{};
for (u32 s = 0; s < num_subsets; ++s)
pbits[s] = reader.read(1);
for (u32 s = 0; s < num_subsets; ++s) {
for (u32 e = 0; e < 2; ++e) {
u32 ep = s * 2 + e;
for (u32 ch = 0; ch < 3; ++ch)
endpoints[ep][ch] = (endpoints[ep][ch] << 1) | pbits[s];
if (mode_info.alpha_bits)
endpoints[ep][3] = (endpoints[ep][3] << 1) | pbits[s];
}
}
const u32 color_prec = mode_info.color_bits + 1;
const u32 alpha_prec = mode_info.alpha_bits ? mode_info.alpha_bits + 1 : 0;
for (u32 ep = 0; ep < num_ep; ++ep) {
for (u32 ch = 0; ch < 3; ++ch)
endpoints[ep][ch] = bc7_unquantize(endpoints[ep][ch], color_prec);
if (mode_info.alpha_bits)
endpoints[ep][3] = bc7_unquantize(endpoints[ep][3], alpha_prec);
else
endpoints[ep][3] = 255;
}
} else {
for (u32 ep = 0; ep < num_ep; ++ep) {
for (u32 ch = 0; ch < 3; ++ch)
endpoints[ep][ch] = bc7_unquantize(endpoints[ep][ch], mode_info.color_bits);
if (mode_info.alpha_bits)
endpoints[ep][3] = bc7_unquantize(endpoints[ep][3], mode_info.alpha_bits);
else
endpoints[ep][3] = 255;
}
}
const u8* part_table = nullptr;
std::array<u32, 3> anchor = {0, 0, 0};
if (num_subsets == 1) {
} else if (num_subsets == 2) {
part_table = BC7_PARTITION_TABLE_2[partition].data();
anchor[1] = BC7_ANCHOR_2[partition];
} else {
part_table = BC7_PARTITION_TABLE_3[partition].data();
anchor[1] = BC7_ANCHOR_3A[partition];
anchor[2] = BC7_ANCHOR_3B[partition];
}
const u32* weight_table1;
u32 primary_bits = mode_info.index_bits;
if (primary_bits == 2)
weight_table1 = BCN_WEIGHT_2.data();
else if (primary_bits == 3)
weight_table1 = BCN_WEIGHT_3.data();
else
weight_table1 = BCN_WEIGHT_4.data();
const u32* weight_table2 = nullptr;
u32 secondary_bits = mode_info.index_bits2;
if (secondary_bits == 2)
weight_table2 = BCN_WEIGHT_2.data();
else if (secondary_bits == 3)
weight_table2 = BCN_WEIGHT_3.data();
std::array<u32, 16> primary_indices{};
for (u32 i = 0; i < 16; ++i) {
u32 s = part_table ? part_table[i] : 0;
bool is_anchor = (i == anchor[s]);
u32 bits = is_anchor ? (primary_bits - 1) : primary_bits;
primary_indices[i] = reader.read(bits);
}
std::array<u32, 16> secondary_indices{};
if (secondary_bits) {
for (u32 i = 0; i < 16; ++i) {
bool is_anchor = (i == 0);
u32 bits = is_anchor ? (secondary_bits - 1) : secondary_bits;
secondary_indices[i] = reader.read(bits);
}
}
for (u32 i = 0; i < 16; ++i) {
u32 s = part_table ? part_table[i] : 0;
u32 e0_idx = s * 2;
u32 e1_idx = s * 2 + 1;
u8 r, g, b, a;
if (secondary_bits) {
u32 color_index = idx_sel ? secondary_indices[i] : primary_indices[i];
u32 alpha_index = idx_sel ? primary_indices[i] : secondary_indices[i];
const u32* color_weight_table = idx_sel ? weight_table2 : weight_table1;
const u32* alpha_weight_table = idx_sel ? weight_table1 : weight_table2;
u32 color_weight = color_weight_table[color_index];
u32 alpha_weight = alpha_weight_table[alpha_index];
r = static_cast<u8>(bcn_interpolate(endpoints[e0_idx][0], endpoints[e1_idx][0], color_weight));
g = static_cast<u8>(bcn_interpolate(endpoints[e0_idx][1], endpoints[e1_idx][1], color_weight));
b = static_cast<u8>(bcn_interpolate(endpoints[e0_idx][2], endpoints[e1_idx][2], color_weight));
a = static_cast<u8>(bcn_interpolate(endpoints[e0_idx][3], endpoints[e1_idx][3], alpha_weight));
} else {
u32 w = weight_table1[primary_indices[i]];
r = static_cast<u8>(bcn_interpolate(endpoints[e0_idx][0], endpoints[e1_idx][0], w));
g = static_cast<u8>(bcn_interpolate(endpoints[e0_idx][1], endpoints[e1_idx][1], w));
b = static_cast<u8>(bcn_interpolate(endpoints[e0_idx][2], endpoints[e1_idx][2], w));
a = static_cast<u8>(bcn_interpolate(endpoints[e0_idx][3], endpoints[e1_idx][3], w));
}
switch (rotation) {
case 1:
std::swap(a, r);
break;
case 2:
std::swap(a, g);
break;
case 3:
std::swap(a, b);
break;
default:
break;
}
out[i * 4 + 0] = r;
out[i * 4 + 1] = g;
out[i * 4 + 2] = b;
out[i * 4 + 3] = a;
}
}
}
std::optional<Texture> decodeTexture(const Texture& src, std::string* out_error,
interfaces::WorkerPool* pool) {
return transform_texture_impl(
src, PixelFormat::BC7, PixelFormat::RGBA8, "bc7::decodeTexture",
[pool](std::span<const u8> data, u32 w, u32 h) {
return decode_image_rgba8<16>(data, w, h, decode_block, pool);
},
out_error);
}