Use diagonal endpoint references for ETC1 encoding
This change slightly improves compression ratio for ETC1 encoding, and also demonstrates how to adjust the endpoint reference configuration for a specific texture format.
Note: This modification alters the output file format for ETC1 encoding and makes it incompatible with the previous revisions.
Explanation:
In addition to the standard endpoint references (to the top and to the left ETC1 blocks), it is also possible to use an endpoint reference to the top-left diagonal neighbour ETC1 block. Specifically, the first ETC1 subblock will now have the reference value of 3 if the endpoint is copied from the second subblock of the top-left neighbour ETC1 block.
DXT 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 (revision ea9b8d8).
[Compressing Kodak set without mipmaps using DXT1 encoding]
Original: 1582222 bytes / 28.895 sec
Modified: 1473711 bytes / 13.356 sec
Improvement: 6.86% (compression ratio) / 53.78% (compression time)
[Compressing Kodak set with mipmaps using DXT1 encoding]
Original: 2065243 bytes / 36.979 sec
Modified: 1920600 bytes / 18.083 sec
Improvement: 7.00% (compression ratio) / 51.10% (compression time)
ETC 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). The ETC1 quantization parameters have been selected in such a way, so that ETC1 compression gives approximately the same average Luma PSNR as the corresponding DXT1 compression (which is equal to 34.044 dB for the Kodak test set compressed without mipmaps using DXT1 encoding and default quality settings).
[Compressing Kodak set without mipmaps using ETC1 encoding]
Total size: 1633207 bytes
Total time: 17.434 sec
Average bitrate: 1.384 bpp
Average Luma PSNR: 34.057 dB
This commit is contained in:
Binary file not shown.
@@ -210,15 +210,17 @@ bool dxt_hc::compress(
|
||||
for (uint bx = 0; bx < block_width; bx++, b++) {
|
||||
bool top_match = by != 0;
|
||||
bool left_match = top_match || bx;
|
||||
bool diag_match = m_params.m_format == cETC1 && top_match && bx;
|
||||
for (uint c = m_has_color_blocks ? 0 : cAlpha0; c < cAlpha0 + m_num_alpha_blocks; c++) {
|
||||
uint16 endpoint_index = (c ? alpha_endpoints_remap : color_endpoints_remap)[m_endpoint_indices[b].component[c]];
|
||||
left_match = left_match && endpoint_index == endpoint_indices[b - 1].component[c];
|
||||
top_match = top_match && endpoint_index == endpoint_indices[b - block_width].component[c];
|
||||
diag_match = diag_match && endpoint_index == endpoint_indices[b - block_width - 1].component[c];
|
||||
endpoint_indices[b].component[c] = endpoint_index;
|
||||
uint16 selector_index = (c ? alpha_selectors_remap : color_selectors_remap)[m_selector_indices[b].component[c]];
|
||||
selector_indices[b].component[c] = selector_index;
|
||||
}
|
||||
endpoint_indices[b].reference = m_params.m_format == cETC1 && (b & 1) ? m_endpoint_indices[b].reference : left_match ? 1 : top_match ? 2 : 0;
|
||||
endpoint_indices[b].reference = m_params.m_format == cETC1 && (b & 1) ? m_endpoint_indices[b].reference : left_match ? 1 : top_match ? 2 : diag_match ? 3 : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-4
@@ -3550,10 +3550,10 @@ class crn_unpacker {
|
||||
const uint32 height = output_height + 1 & ~1;
|
||||
const int32 delta_pitch_in_dwords = (output_pitch_in_bytes >> 2) - (width << 1);
|
||||
|
||||
if (m_block_buffer.size() < width)
|
||||
m_block_buffer.resize(width);
|
||||
if (m_block_buffer.size() < width << 1)
|
||||
m_block_buffer.resize(width << 1);
|
||||
|
||||
uint32 color_endpoint_index = 0;
|
||||
uint32 color_endpoint_index = 0, diagonal_color_endpoint_index = 0;
|
||||
uint8 reference_group = 0;
|
||||
|
||||
for (uint32 f = 0; f < m_pHeader->m_faces; f++) {
|
||||
@@ -3562,7 +3562,7 @@ class crn_unpacker {
|
||||
bool visible = y < output_height;
|
||||
for (uint32 x = 0; x < width; x++, pData += 2) {
|
||||
visible = visible && x < output_width;
|
||||
block_buffer_element &buffer = m_block_buffer[x];
|
||||
block_buffer_element &buffer = m_block_buffer[x << 1];
|
||||
uint8 endpoint_reference, block_endpoint[4], e0[4], e1[4];
|
||||
if (y & 1) {
|
||||
endpoint_reference = buffer.endpoint_reference;
|
||||
@@ -3578,6 +3578,8 @@ class crn_unpacker {
|
||||
buffer.color_endpoint_index = color_endpoint_index;
|
||||
} else if ((endpoint_reference & 3) == 1) {
|
||||
buffer.color_endpoint_index = color_endpoint_index;
|
||||
} else if ((endpoint_reference & 3) == 3) {
|
||||
buffer.color_endpoint_index = color_endpoint_index = diagonal_color_endpoint_index;
|
||||
} else {
|
||||
color_endpoint_index = buffer.color_endpoint_index;
|
||||
}
|
||||
@@ -3589,6 +3591,8 @@ class crn_unpacker {
|
||||
if (color_endpoint_index >= num_color_endpoints)
|
||||
color_endpoint_index -= num_color_endpoints;
|
||||
}
|
||||
diagonal_color_endpoint_index = m_block_buffer[x << 1 | 1].color_endpoint_index;
|
||||
m_block_buffer[x << 1 | 1].color_endpoint_index = color_endpoint_index;
|
||||
*(uint32*)&e1 = m_color_endpoints[color_endpoint_index];
|
||||
if (visible) {
|
||||
uint32 block_selector = 0, flip = endpoint_reference >> 1 ^ 1, diff = 1;
|
||||
|
||||
Reference in New Issue
Block a user