Switch from chunk encoding to block encoding after quantization

This change simplifies further modification of the code.

Explanation:
Considering that chunks are no longer used in the output format, it makes sense to also remove chunk related code from the intermediate processing. This modification also allows to use endpoint references from the leftmost block to the rightmost block in the previous scanline (wrapped reference to the left).

Testing:
The modified algorithm has been tested on the Kodak test set using 64-bit build with default settings (running on Windows 10, i7-4790, 3.6GHz). All the decompressed test images are identical to the images being compressed and decompressed using original version of Crunch.

[Compressing Kodak set without mipmaps]
Original: 1582222 bytes / 28.846 sec
Modified: 1494501 bytes / 25.628 sec
Improvement: 5.54% (compression ratio) / 11.16% (compression time)

[Compressing Kodak set with mipmaps]
Original: 2065243 bytes / 36.869 sec
Modified: 1945365 bytes / 33.497 sec
Improvement: 5.80% (compression ratio) / 9.15% (compression time)
This commit is contained in:
Alexander Suvorov
2017-05-09 17:34:21 +02:00
parent 5258727545
commit d0b6f5759b
5 changed files with 124 additions and 255 deletions
+53 -120
View File
@@ -690,41 +690,38 @@ bool crn_comp::pack_chunks(
}
}
for (uint chunk_base = first_chunk; chunk_base < first_chunk + num_chunks; chunk_base += chunk_width) {
for (uint by = 0; by < 2; by++) {
for (uint cx = 0; cx < chunk_width; cx++) {
const chunk_detail& details = m_chunk_details[chunk_base + cx];
if (!by) {
if (pCodec)
pCodec->encode(details.m_reference_group, m_reference_encoding_dm);
else
m_chunk_encoding_hist.inc_freq(details.m_reference_group);
for (uint by = 0, block_width = chunk_width << 1, b = first_chunk << 2, bEnd = b + (num_chunks << 2); b < bEnd; by++) {
for (uint bx = 0; bx < block_width; bx++, b++) {
if (!(by & 1) && !(bx & 1)) {
uint8 reference_group = m_endpoint_indices[b].reference | m_endpoint_indices[b + block_width].reference << 2 |
m_endpoint_indices[b + 1].reference << 4 | m_endpoint_indices[b + block_width + 1].reference << 6;
if (pCodec)
pCodec->encode(reference_group, m_reference_encoding_dm);
else
m_chunk_encoding_hist.inc_freq(reference_group);
}
for (uint c = 0; c < cNumComps; c++) {
if (endpoint_remap[c]) {
uint index = (*endpoint_remap[c])[m_endpoint_indices[b].component[c]];
if (!m_endpoint_indices[b].reference) {
int sym = index - endpoint_index[c];
if (sym < 0)
sym += endpoint_remap[c]->size();
if (!pCodec)
m_endpoint_index_hist[c ? 1 : 0].inc_freq(sym);
else
pCodec->encode(sym, m_endpoint_index_dm[c ? 1 : 0]);
}
endpoint_index[c] = index;
}
for (uint bx = 0; bx < 2; bx++) {
for (uint c = 0; c < cNumComps; c++) {
if (endpoint_remap[c]) {
uint index = (*endpoint_remap[c])[details.m_endpoint_indices[by][bx][c]];
if (!details.m_endpoint_references[by][bx]) {
int sym = index - endpoint_index[c];
if (sym < 0)
sym += endpoint_remap[c]->size();
if (!pCodec)
m_endpoint_index_hist[c ? 1 : 0].inc_freq(sym);
else
pCodec->encode(sym, m_endpoint_index_dm[c ? 1 : 0]);
}
endpoint_index[c] = index;
}
}
for (uint c = 0; c < cNumComps; c++) {
if (selector_remap[c]) {
uint index = (*selector_remap[c])[details.m_selector_indices[by][bx][c]];
if (!pCodec)
m_selector_index_hist[c ? 1 : 0].inc_freq(index);
else
pCodec->encode(index, m_selector_index_dm[c ? 1 : 0]);
}
}
}
for (uint c = 0; c < cNumComps; c++) {
if (selector_remap[c]) {
uint index = (*selector_remap[c])[m_selector_indices[b].component[c]];
if (!pCodec)
m_selector_index_hist[c ? 1 : 0].inc_freq(index);
else
pCodec->encode(index, m_selector_index_dm[c ? 1 : 0]);
}
}
}
@@ -733,7 +730,6 @@ bool crn_comp::pack_chunks(
}
bool crn_comp::pack_chunks_simulation(
uint first_chunk, uint num_chunks,
uint& total_bits,
const crnlib::vector<uint>* pColor_endpoint_remap,
const crnlib::vector<uint>* pColor_selector_remap,
@@ -945,7 +941,8 @@ void crn_comp::clear() {
utils::zero_object(m_has_comp);
m_chunk_details.clear();
m_endpoint_indices.clear();
m_selector_indices.clear();
m_total_chunks = 0;
@@ -1106,70 +1103,18 @@ bool crn_comp::quantize_chunks() {
for (uint i = 0; i < m_pParams->m_levels; i++) {
params.m_levels[i].m_first_chunk = m_levels[i].m_first_chunk;
params.m_levels[i].m_num_chunks = m_levels[i].m_num_chunks;
params.m_levels[i].m_chunk_width = m_levels[i].m_chunk_width;
}
params.m_endpoint_indices = &m_endpoint_indices;
params.m_selector_indices = &m_selector_indices;
if (!m_hvq.compress(params, m_total_chunks, &m_chunks[0], m_task_pool))
return false;
#if CRNLIB_CREATE_DEBUG_IMAGES
if (params.m_debugging) {
const dxt_hc::pixel_chunk_vec& pixel_chunks = m_hvq.get_compressed_chunk_pixels_final();
image_u8 img;
dxt_hc::create_debug_image_from_chunks((m_pParams->m_width + 7) >> 3, (m_pParams->m_height + 7) >> 3, pixel_chunks, &m_hvq.get_chunk_encoding_vec(), img, true, -1);
image_utils::write_to_file("quantized_chunks.tga", img);
}
#endif
return true;
}
void crn_comp::create_chunk_indices() {
uint8 endpoint_index_map[8][4] = {
{ 0, 0, 0, 0 },
{ 0, 0, 1, 1 },
{ 0, 1, 0, 1 },
{ 0, 0, 1, 2 },
{ 1, 2, 0, 0 },
{ 0, 1, 0, 2 },
{ 1, 0, 2, 0 },
{ 0, 1, 2, 3 },
};
m_chunk_details.resize(m_total_chunks);
for (uint group = 0; group < m_mip_groups.size(); group++) {
uint chunk_width = m_mip_groups[group].m_chunk_width;
for (uint i = 0; i < m_mip_groups[group].m_num_chunks;) {
for (uint cx = 0; cx < chunk_width; cx++, i++) {
uint chunk_index = m_mip_groups[group].m_first_chunk + i, left_chunk_index = 0, top_chunk_index = 0;
const dxt_hc::chunk_encoding& chunk_encoding = m_hvq.get_chunk_encoding(chunk_index);
for (uint t = 0, by = 0; by < 2; by++) {
for (uint bx = 0; bx < 2; bx++, t++) {
bool left_match = bx || cx;
if (left_match)
left_chunk_index = bx ? chunk_index : chunk_index - 1;
bool top_match = by || i >= chunk_width;
if (top_match)
top_chunk_index = by ? chunk_index : chunk_index - chunk_width;
for (uint c = 0; c < cNumComps; c++) {
if (m_has_comp[c]) {
m_chunk_details[chunk_index].m_endpoint_indices[by][bx][c] = chunk_encoding.m_endpoint_indices[c][endpoint_index_map[chunk_encoding.m_encoding_index][t]];
left_match = left_match && m_chunk_details[chunk_index].m_endpoint_indices[by][bx][c] == m_chunk_details[left_chunk_index].m_endpoint_indices[by][bx ^ 1][c];
top_match = top_match && m_chunk_details[chunk_index].m_endpoint_indices[by][bx][c] == m_chunk_details[top_chunk_index].m_endpoint_indices[by ^ 1][bx][c];
m_chunk_details[chunk_index].m_selector_indices[by][bx][c] = chunk_encoding.m_selector_indices[c][by][bx];
}
}
uint8 endpoint_reference = left_match ? 1 : top_match ? 2 : 0;
m_chunk_details[chunk_index].m_endpoint_references[by][bx] = endpoint_reference;
m_chunk_details[chunk_index].m_reference_group |= endpoint_reference << (bx << 2 | by << 1);
}
}
}
}
}
}
struct optimize_color_endpoint_codebook_params {
crnlib::vector<uint>* m_pTrial_color_endpoint_remap;
uint m_iter_index;
@@ -1223,17 +1168,10 @@ bool crn_comp::optimize_color_endpoint_codebook(crnlib::vector<uint>& remapping)
uint n = m_hvq.get_color_endpoint_codebook_size();
hist_type xhist(n * n);
uint endpoint_index[2][2] = {};
for (uint chunk_index = 0; chunk_index < m_chunks.size(); chunk_index++) {
const chunk_detail& details = m_chunk_details[chunk_index];
for (uint y = 0; y < 2; y++) {
for (uint x = 0; x < 2; x++) {
endpoint_index[y][x] = details.m_endpoint_indices[y][x][cColor];
if (!details.m_endpoint_references[y][x]) {
update_hist(xhist, endpoint_index[y][x], endpoint_index[y][x ^ 1], n);
update_hist(xhist, endpoint_index[y][x ^ 1], endpoint_index[y][x], n);
}
}
for (uint b = 1; b < m_endpoint_indices.size(); b++) {
if (!m_endpoint_indices[b].reference) {
update_hist(xhist, m_endpoint_indices[b - 1].color, m_endpoint_indices[b].color, n);
update_hist(xhist, m_endpoint_indices[b].color, m_endpoint_indices[b - 1].color, n);
}
}
@@ -1260,7 +1198,7 @@ bool crn_comp::optimize_color_endpoint_codebook(crnlib::vector<uint>& remapping)
return false;
uint total_packed_chunk_bits;
if (!pack_chunks_simulation(0, m_total_chunks, total_packed_chunk_bits, &trial_color_endpoint_remap, NULL, NULL, NULL))
if (!pack_chunks_simulation(total_packed_chunk_bits, &trial_color_endpoint_remap, NULL, NULL, NULL))
return false;
#if CRNLIB_ENABLE_DEBUG_MESSAGES
@@ -1353,19 +1291,16 @@ bool crn_comp::optimize_alpha_endpoint_codebook(crnlib::vector<uint>& remapping)
uint n = m_hvq.get_alpha_endpoint_codebook_size();
hist_type xhist(n * n);
uint endpoint_index[2][2][cNumComps] = {};
uint min_comp_index = m_has_comp[cAlpha0] ? cAlpha0 : cAlpha1, max_comp_index = m_has_comp[cAlpha1] ? cAlpha1 : cAlpha0;
for (uint chunk_index = 0; chunk_index < m_chunks.size(); chunk_index++) {
const chunk_detail& details = m_chunk_details[chunk_index];
for (uint y = 0; y < 2; y++) {
for (uint x = 0; x < 2; x++) {
for (uint comp_index = min_comp_index; comp_index <= max_comp_index; comp_index++) {
endpoint_index[y][x][comp_index] = details.m_endpoint_indices[y][x][comp_index];
if (!details.m_endpoint_references[y][x]) {
update_hist(xhist, endpoint_index[y][x][comp_index], endpoint_index[y][x ^ 1][comp_index], n);
update_hist(xhist, endpoint_index[y][x ^ 1][comp_index], endpoint_index[y][x][comp_index], n);
}
}
bool hasAlpha0 = m_has_comp[cAlpha0], hasAlpha1 = m_has_comp[cAlpha1];
for (uint b = 1; b < m_endpoint_indices.size(); b++) {
if (!m_endpoint_indices[b].reference) {
if (hasAlpha0) {
update_hist(xhist, m_endpoint_indices[b - 1].alpha0, m_endpoint_indices[b].alpha0, n);
update_hist(xhist, m_endpoint_indices[b].alpha0, m_endpoint_indices[b - 1].alpha0, n);
}
if (hasAlpha1) {
update_hist(xhist, m_endpoint_indices[b - 1].alpha1, m_endpoint_indices[b].alpha1, n);
update_hist(xhist, m_endpoint_indices[b].alpha1, m_endpoint_indices[b - 1].alpha1, n);
}
}
}
@@ -1393,7 +1328,7 @@ bool crn_comp::optimize_alpha_endpoint_codebook(crnlib::vector<uint>& remapping)
return false;
uint total_packed_chunk_bits;
if (!pack_chunks_simulation(0, m_total_chunks, total_packed_chunk_bits, NULL, NULL, &trial_alpha_endpoint_remap, NULL))
if (!pack_chunks_simulation(total_packed_chunk_bits, NULL, NULL, &trial_alpha_endpoint_remap, NULL))
return false;
#if CRNLIB_ENABLE_DEBUG_MESSAGES
@@ -1557,8 +1492,6 @@ bool crn_comp::compress_internal() {
if (!quantize_chunks())
return false;
create_chunk_indices();
crnlib::vector<uint> endpoint_remap[2];
crnlib::vector<uint> selector_remap[2];