Reorder chunks in each scanline in the left-to-right manner

This change slightly improves compression ratio and compression time.

Explanation:
The efficiency of the Crunch encoding scheme depends on the similarity between the neighbour chunks. For this reason in original version of Crunch the order of chunks is reversed after each scanline, so that there is no jump from one side of the image to another at the image borders. The problem here is that inside of each chunk, the blocks are normally ordered in a usual up-to-down-left-to-right manner, regardless of the chunk scanning order. While on the forward scan we normally need to perform diagonal jumps (+1, +1) in order to get to the next chunk, on the reverse scan we normally need to perform much larger (-3, +1) jumps, which usually defeats the advantage of not having discontinuity at the image borders.

Note:
This modification alters the output format and makes it incompatible with the previous revisions.

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.882 sec
Modified: 1579618 bytes / 28.743 sec
Improvement: 0.16% (compression ratio) / 0.48% (compression time)

[Compressing Kodak set with mipmaps]
Original: 2065243 bytes / 36.920 sec
Modified: 2061499 bytes / 36.833 sec
Improvement: 0.18% (compression ratio) / 0.24% (compression time)
This commit is contained in:
Alexander Suvorov
2017-04-27 10:59:53 +02:00
parent 5d09a511d5
commit 13b1faa48d
5 changed files with 11 additions and 68 deletions
Binary file not shown.
+2 -10
View File
@@ -1011,20 +1011,12 @@ bool crn_comp::alias_images() {
void crn_comp::append_chunks(const image_u8& img, uint num_chunks_x, uint num_chunks_y, dxt_hc::pixel_chunk_vec& chunks, float weight) {
for (uint y = 0; y < num_chunks_y; y++) {
int x_start = 0;
int x_end = num_chunks_x;
int x_dir = 1;
if (y & 1) {
x_start = num_chunks_x - 1;
x_end = -1;
x_dir = -1;
}
for (int x = x_start; x != x_end; x += x_dir) {
for (uint legacy_index = chunks.size(), x = 0; x < num_chunks_x; x++) {
chunks.resize(chunks.size() + 1);
dxt_hc::pixel_chunk& chunk = chunks.back();
chunk.m_weight = weight;
chunk.m_legacy_index = legacy_index + (y & 1 ? num_chunks_x - 1 - x : x);
for (uint cy = 0; cy < cChunkPixelHeight; cy++) {
uint py = y * cChunkPixelHeight + cy;
+4 -6
View File
@@ -754,12 +754,11 @@ bool dxt_hc::determine_color_endpoint_clusters() {
if (m_canceled)
return false;
for (uint chunk_index = 0; chunk_index < m_num_chunks; chunk_index++) {
for (uint i = 0; i < m_num_chunks; i++) {
int chunk_index = m_pChunks[i].m_legacy_index;
compressed_chunk& chunk = m_compressed_chunks[cColorChunks][chunk_index];
for (uint tile_index = 0; tile_index < chunk.m_num_tiles; tile_index++) {
uint cluster_index = chunk.m_endpoint_cluster_index[tile_index];
m_color_clusters[cluster_index].m_tiles.push_back(std::make_pair(chunk_index, tile_index));
}
}
@@ -899,12 +898,11 @@ bool dxt_hc::determine_alpha_endpoint_clusters() {
return false;
for (uint a = 0; a < m_num_alpha_blocks; a++) {
for (uint chunk_index = 0; chunk_index < m_num_chunks; chunk_index++) {
for (uint i = 0; i < m_num_chunks; i++) {
int chunk_index = m_pChunks[i].m_legacy_index;
compressed_chunk& chunk = m_compressed_chunks[cAlpha0Chunks + a][chunk_index];
for (uint tile_index = 0; tile_index < chunk.m_num_tiles; tile_index++) {
const uint cluster_index = chunk.m_endpoint_cluster_index[tile_index];
m_alpha_clusters[cluster_index].m_tiles.push_back(std::make_pair(chunk_index, tile_index | (a << 16)));
}
}
+1
View File
@@ -47,6 +47,7 @@ class dxt_hc {
}
float m_weight;
uint m_legacy_index;
};
typedef crnlib::vector<pixel_chunk> pixel_chunk_vec;
+4 -52
View File
@@ -3538,23 +3538,11 @@ class crn_unpacker {
uint8* CRND_RESTRICT pRow = pDst[f];
for (uint32 y = 0; y < chunks_y; y++) {
int32 start_x = 0;
int32 end_x = chunks_x;
int32 dir_x = 1;
int32 block_delta = cBytesPerBlock * 2;
uint8* CRND_RESTRICT pBlock = pRow;
if (y & 1) {
start_x = chunks_x - 1;
end_x = -1;
dir_x = -1;
block_delta = -cBytesPerBlock * 2;
pBlock += (chunks_x - 1) * cBytesPerBlock * 2;
}
const bool skip_bottom_row = (y == (chunks_y - 1)) && (blocks_y & 1);
for (int32 x = start_x; x != end_x; x += dir_x) {
for (uint32 x = 0; x < chunks_x; x++) {
uint32 color_endpoints[4];
if (chunk_encoding_bits == 1) {
@@ -3638,23 +3626,11 @@ class crn_unpacker {
uint8* CRND_RESTRICT pRow = pDst[f];
for (uint32 y = 0; y < chunks_y; y++) {
int32 start_x = 0;
int32 end_x = chunks_x;
int32 dir_x = 1;
int32 block_delta = cBytesPerBlock * 2;
uint8* CRND_RESTRICT pBlock = pRow;
if (y & 1) {
start_x = chunks_x - 1;
end_x = -1;
dir_x = -1;
block_delta = -cBytesPerBlock * 2;
pBlock += (chunks_x - 1) * cBytesPerBlock * 2;
}
const bool skip_bottom_row = (y == (chunks_y - 1)) && (blocks_y & 1);
for (int32 x = start_x; x != end_x; x += dir_x) {
for (uint32 x = 0; x < chunks_x; x++) {
uint32 color_endpoints[4];
uint32 alpha_endpoints[4];
@@ -3757,23 +3733,11 @@ class crn_unpacker {
uint8* CRND_RESTRICT pRow = pDst[f];
for (uint32 y = 0; y < chunks_y; y++) {
int32 start_x = 0;
int32 end_x = chunks_x;
int32 dir_x = 1;
int32 block_delta = cBytesPerBlock * 2;
uint8* CRND_RESTRICT pBlock = pRow;
if (y & 1) {
start_x = chunks_x - 1;
end_x = -1;
dir_x = -1;
block_delta = -cBytesPerBlock * 2;
pBlock += (chunks_x - 1) * cBytesPerBlock * 2;
}
const bool skip_bottom_row = (y == (chunks_y - 1)) && (blocks_y & 1);
for (int32 x = start_x; x != end_x; x += dir_x) {
for (uint32 x = 0; x < chunks_x; x++) {
uint32 alpha0_endpoints[4];
uint32 alpha1_endpoints[4];
@@ -3873,23 +3837,11 @@ class crn_unpacker {
uint8* CRND_RESTRICT pRow = pDst[f];
for (uint32 y = 0; y < chunks_y; y++) {
int32 start_x = 0;
int32 end_x = chunks_x;
int32 dir_x = 1;
int32 block_delta = cBytesPerBlock * 2;
uint8* CRND_RESTRICT pBlock = pRow;
if (y & 1) {
start_x = chunks_x - 1;
end_x = -1;
dir_x = -1;
block_delta = -cBytesPerBlock * 2;
pBlock += (chunks_x - 1) * cBytesPerBlock * 2;
}
const bool skip_bottom_row = (y == (chunks_y - 1)) && (blocks_y & 1);
for (int32 x = start_x; x != end_x; x += dir_x) {
for (uint32 x = 0; x < chunks_x; x++) {
uint32 alpha0_endpoints[4];
if (chunk_encoding_bits == 1) {