u64 estimate_partition_cost(const u8* rgba, u32 partition) {
const u8* part = BC7_PARTITION_TABLE_2[partition].data();
std::array<RGBA, 2> min_color = {{{255, 255, 255, 255}, {255, 255, 255, 255}}};
std::array<RGBA, 2> max_color = {{{0, 0, 0, 0}, {0, 0, 0, 0}}};
for (u32 i = 0; i < 16; ++i) {
u8 s = part[i];
auto p = pixel_at(rgba, i);
min_color[s].r = std::min(min_color[s].r, p.r);
min_color[s].g = std::min(min_color[s].g, p.g);
min_color[s].b = std::min(min_color[s].b, p.b);
max_color[s].r = std::max(max_color[s].r, p.r);
max_color[s].g = std::max(max_color[s].g, p.g);
max_color[s].b = std::max(max_color[s].b, p.b);
}
u64 cost = 0;
for (u32 s = 0; s < 2; ++s) {
u64 dr = max_color[s].r - min_color[s].r;
u64 dg = max_color[s].g - min_color[s].g;
u64 db = max_color[s].b - min_color[s].b;
cost += dr * dr + dg * dg + db * db;
}
return cost;
}
std::array<u32, 4> best_partitions(const u8* rgba, u32 top_n) {
struct PC {
u32 idx = 0;
u64 cost = 0;
};
std::array<PC, 64> costs;
for (u32 i = 0; i < 64; ++i)
costs[i] = {i, estimate_partition_cost(rgba, i)};
std::sort(costs.begin(), costs.end(), [](const PC& a, const PC& b) { return a.cost < b.cost; });
std::array<u32, 4> result{};
for (u32 i = 0; i < std::min(top_n, 4u); ++i)
result[i] = costs[i].idx;
return result;
}
u64 estimate_partition_cost_3(const u8* rgba, u32 partition) {
const u8* part = BC7_PARTITION_TABLE_3[partition].data();
std::array<RGBA, 3> min_color = {{{255, 255, 255, 255}, {255, 255, 255, 255}, {255, 255, 255, 255}}};
std::array<RGBA, 3> max_color = {{{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}};
for (u32 i = 0; i < 16; ++i) {
u8 s = part[i];
auto p = pixel_at(rgba, i);
min_color[s].r = std::min(min_color[s].r, p.r);
min_color[s].g = std::min(min_color[s].g, p.g);
min_color[s].b = std::min(min_color[s].b, p.b);
max_color[s].r = std::max(max_color[s].r, p.r);
max_color[s].g = std::max(max_color[s].g, p.g);
max_color[s].b = std::max(max_color[s].b, p.b);
}
u64 cost = 0;
for (u32 s = 0; s < 3; ++s) {
u64 dr = max_color[s].r - min_color[s].r;
u64 dg = max_color[s].g - min_color[s].g;
u64 db = max_color[s].b - min_color[s].b;
cost += dr * dr + dg * dg + db * db;
}
return cost;
}
std::array<u32, 4> best_partitions_3(const u8* rgba, u32 num_partitions, u32 top_n) {
struct PC {
u32 idx = 0;
u64 cost = 0;
};
std::array<PC, 64> costs;
for (u32 i = 0; i < num_partitions; ++i)
costs[i] = {i, estimate_partition_cost_3(rgba, i)};
std::sort(costs.begin(), costs.begin() + num_partitions,
[](const PC& a, const PC& b) { return a.cost < b.cost; });
std::array<u32, 4> result{};
for (u32 i = 0; i < std::min(top_n, 4u); ++i)
result[i] = costs[i].idx;
return result;
}
void encode_block(const u8* rgba, u8* out, Quality quality) {
std::array<u8, 16> buf{};
bool use_pca = quality >= Quality::Normal;
u64 best_err = encode_mode6(rgba, buf.data(), use_pca);
std::memcpy(out, buf.data(), 16);
auto try_update = [&](u64 err) {
if (err < best_err) {
best_err = err;
std::memcpy(out, buf.data(), 16);
}
};
if (quality >= Quality::Normal) {
try_update(encode_mode5(rgba, buf.data(), use_pca));
try_update(encode_mode4(rgba, buf.data(), use_pca));
}
if (quality >= Quality::High) {
auto parts2 = best_partitions(rgba, 4);
for (u32 t = 0; t < 4; ++t) {
try_update(encode_mode1_partition(rgba, buf.data(), parts2[t]));
try_update(encode_mode3_partition(rgba, buf.data(), parts2[t]));
try_update(encode_mode7_partition(rgba, buf.data(), parts2[t]));
}
auto parts3_m0 = best_partitions_3(rgba, 16, 4);
for (u32 t = 0; t < 4; ++t)
try_update(encode_mode0_partition(rgba, buf.data(), parts3_m0[t]));
auto parts3_m2 = best_partitions_3(rgba, 64, 4);
for (u32 t = 0; t < 4; ++t)
try_update(encode_mode2_partition(rgba, buf.data(), parts3_m2[t]));
}
}
}
std::optional<Texture> encodeTexture(const Texture& src, Quality quality, std::string* out_error,
interfaces::WorkerPool* pool) {
return transform_texture_impl(
src, PixelFormat::RGBA8, PixelFormat::BC7, "bc7::encodeTexture",
[quality, pool](std::span<const u8> data, u32 w, u32 h) {
return encode_image_rgba8<16>(data, w, h,
[quality](const u8* b, u8* o) { encode_block(b, o, quality); }, pool);
},
out_error);
}