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
Binary file not shown.
+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];
+2 -10
View File
@@ -65,14 +65,8 @@ class crn_comp : public itexture_comp {
bool m_has_comp[cNumComps];
struct chunk_detail {
chunk_detail() { utils::zero_object(*this); }
uint16 m_endpoint_indices[2][2][cNumComps];
uint16 m_selector_indices[2][2][cNumComps];
uint8 m_endpoint_references[2][2];
uint8 m_reference_group;
};
crnlib::vector<chunk_detail> m_chunk_details;
crnlib::vector<dxt_hc::endpoint_indices_details> m_endpoint_indices;
crnlib::vector<dxt_hc::selector_indices_details> m_selector_indices;
uint m_total_chunks;
dxt_hc::pixel_chunk_vec m_chunks;
@@ -125,7 +119,6 @@ class crn_comp : public itexture_comp {
bool alias_images();
void create_chunks();
bool quantize_chunks();
void create_chunk_indices();
bool pack_chunks(
uint group,
@@ -137,7 +130,6 @@ class crn_comp : public itexture_comp {
const crnlib::vector<uint>* pAlpha_selector_remap);
bool 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,
+41 -97
View File
@@ -46,8 +46,6 @@ void dxt_hc::clear() {
m_num_chunks = 0;
m_pChunks = NULL;
m_chunk_encoding.clear();
m_num_alpha_blocks = 0;
m_has_color_blocks = false;
m_has_alpha0_blocks = false;
@@ -194,7 +192,7 @@ bool dxt_hc::compress_internal(const params& p, uint num_chunks, const pixel_chu
create_final_debug_image();
if (!create_chunk_encodings())
if (!create_block_encodings(p))
return false;
return true;
@@ -2287,66 +2285,52 @@ void dxt_hc::create_final_debug_image() {
} // chunk_index
}
bool dxt_hc::create_chunk_encodings() {
m_chunk_encoding.resize(m_num_chunks);
bool dxt_hc::create_block_encodings(const params& p) {
crnlib::vector<endpoint_indices_details>& m_endpoint_indices = *p.m_endpoint_indices;
crnlib::vector<selector_indices_details>& m_selector_indices = *p.m_selector_indices;
for (uint chunk_index = 0; chunk_index < m_num_chunks; chunk_index++) {
if ((chunk_index & 255) == 0) {
if (!update_progress(19, chunk_index, m_num_chunks))
return false;
}
uint8 tile_map[8][2][2] = {
{{ 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 }},
};
chunk_encoding& encoding = m_chunk_encoding[chunk_index];
m_endpoint_indices.resize(m_num_chunks << 2);
m_selector_indices.resize(m_num_chunks << 2);
bool hasBlocks[cNumCompressedChunkVecs] = {m_has_color_blocks, m_num_alpha_blocks > 0, m_num_alpha_blocks > 1};
for (uint q = 0; q < cNumCompressedChunkVecs; q++) {
bool skip = true;
if (q == cColorChunks) {
if (m_has_color_blocks)
skip = false;
} else if (q <= m_num_alpha_blocks)
skip = false;
if (skip)
continue;
CRNLIB_ASSERT(!m_compressed_chunks[q].empty());
const compressed_chunk& chunk = m_compressed_chunks[q][chunk_index];
CRNLIB_ASSERT(chunk.m_encoding_index < cNumChunkEncodings);
encoding.m_encoding_index = static_cast<uint8>(chunk.m_encoding_index);
CRNLIB_ASSERT(chunk.m_num_tiles <= cChunkMaxTiles);
encoding.m_num_tiles = static_cast<uint8>(chunk.m_num_tiles);
for (uint tile_index = 0; tile_index < chunk.m_num_tiles; tile_index++) {
const compressed_tile& quantized_tile = chunk.m_quantized_tiles[tile_index];
if (!q) {
CRNLIB_ASSERT(quantized_tile.m_endpoint_cluster_index < m_color_clusters.size());
} else {
CRNLIB_ASSERT(quantized_tile.m_endpoint_cluster_index < m_alpha_clusters.size());
}
encoding.m_endpoint_indices[q][tile_index] = static_cast<uint16>(quantized_tile.m_endpoint_cluster_index);
}
for (uint y = 0; y < cChunkBlockHeight; y++) {
for (uint x = 0; x < cChunkBlockWidth; x++) {
const uint selector_index = chunk.m_selector_cluster_index[y][x];
if (!q) {
CRNLIB_ASSERT(selector_index < m_color_selectors.size());
} else {
CRNLIB_ASSERT(selector_index < m_alpha_selectors.size());
for (uint level = 0; level < p.m_num_levels; level++) {
uint first_chunk = p.m_levels[level].m_first_chunk;
uint end_chunk = p.m_levels[level].m_first_chunk + p.m_levels[level].m_num_chunks;
uint chunk_width = p.m_levels[level].m_chunk_width;
uint block_width = chunk_width << 1;
for (uint b = first_chunk << 2, cy = 0, chunk_base = first_chunk; chunk_base < end_chunk; chunk_base += chunk_width, cy++) {
for (uint by = 0; by < 2; by++) {
for (uint cx = 0; cx < chunk_width; cx++) {
for (uint bx = 0; bx < 2; bx++, b++) {
bool top_match = cy || by;
bool left_match = top_match || cx || bx;
for (uint c = 0; c < cNumCompressedChunkVecs; c++) {
if (hasBlocks[c]) {
const compressed_chunk& chunk = m_compressed_chunks[c][chunk_base + cx];
uint16 endpoint_index = chunk.m_quantized_tiles[tile_map[chunk.m_encoding_index][by][bx]].m_endpoint_cluster_index;
left_match = left_match && endpoint_index == m_endpoint_indices[b - 1].component[c];
top_match = top_match && endpoint_index == m_endpoint_indices[b - block_width].component[c];
m_endpoint_indices[b].component[c] = endpoint_index;
m_selector_indices[b].component[c] = chunk.m_selector_cluster_index[by][bx];
}
}
m_endpoint_indices[b].reference = left_match ? 1 : top_match ? 2 : 0;
}
encoding.m_selector_indices[q][y][x] = static_cast<uint16>(selector_index);
}
}
} // q
} // chunk_index
}
}
if (m_has_color_blocks) {
m_color_endpoints.resize(m_color_clusters.size());
@@ -2363,46 +2347,6 @@ bool dxt_hc::create_chunk_encodings() {
return true;
}
void dxt_hc::create_debug_image_from_chunks(uint num_chunks_x, uint num_chunks_y, const pixel_chunk_vec& chunks, const chunk_encoding_vec* pChunk_encodings, image_u8& img, bool serpentine_scan, int comp_index) {
if (chunks.empty()) {
img.set_all(color_quad_u8::make_black());
return;
}
img.resize(num_chunks_x * cChunkPixelWidth, num_chunks_y * cChunkPixelHeight);
for (uint y = 0; y < num_chunks_y; y++) {
for (uint x = 0; x < num_chunks_x; x++) {
uint c = x + y * num_chunks_x;
if ((serpentine_scan) && (y & 1))
c = (num_chunks_x - 1 - x) + y * num_chunks_x;
if (comp_index >= 0) {
for (uint cy = 0; cy < cChunkPixelHeight; cy++)
for (uint cx = 0; cx < cChunkPixelWidth; cx++)
img(x * cChunkPixelWidth + cx, y * cChunkPixelHeight + cy) = chunks[c](cx, cy)[comp_index];
} else {
for (uint cy = 0; cy < cChunkPixelHeight; cy++)
for (uint cx = 0; cx < cChunkPixelWidth; cx++)
img(x * cChunkPixelWidth + cx, y * cChunkPixelHeight + cy) = chunks[c](cx, cy);
}
if (pChunk_encodings) {
const chunk_encoding& chunk = (*pChunk_encodings)[c];
const chunk_encoding_desc& encoding_desc = g_chunk_encodings[chunk.m_encoding_index];
CRNLIB_ASSERT(chunk.m_num_tiles == encoding_desc.m_num_tiles);
for (uint t = 0; t < chunk.m_num_tiles; t++) {
const chunk_tile_desc& tile_desc = encoding_desc.m_tiles[t];
img.unclipped_fill_box(
x * 8 + tile_desc.m_x_ofs, y * 8 + tile_desc.m_y_ofs,
tile_desc.m_width + 1, tile_desc.m_height + 1, color_quad_u8(128, 128, 128, 255));
}
}
}
}
}
bool dxt_hc::update_progress(uint phase_index, uint subphase_index, uint subphase_total) {
CRNLIB_ASSERT(crn_get_current_thread_id() == m_main_thread_id);
+28 -28
View File
@@ -22,6 +22,29 @@ class dxt_hc {
dxt_hc();
~dxt_hc();
struct endpoint_indices_details {
union {
struct {
uint16 color;
uint16 alpha0;
uint16 alpha1;
};
uint16 component[3];
};
uint8 reference;
};
struct selector_indices_details {
union {
struct {
uint16 color;
uint16 alpha0;
uint16 alpha1;
};
uint16 component[3];
};
};
struct pixel_chunk {
pixel_chunk() { clear(); }
@@ -99,11 +122,15 @@ class dxt_hc {
struct miplevel_desc {
uint m_first_chunk;
uint m_num_chunks;
uint m_chunk_width;
};
// The mip level data is optional!
miplevel_desc m_levels[cCRNMaxLevels];
uint m_num_levels;
crnlib::vector<endpoint_indices_details> *m_endpoint_indices;
crnlib::vector<selector_indices_details> *m_selector_indices;
dxt_format m_format;
// If m_hierarchical is false, only 4x4 blocks will be used by the encoder (leading to higher quality/larger files).
@@ -126,28 +153,6 @@ class dxt_hc {
// Output accessors
inline uint get_num_chunks() const { return m_num_chunks; }
struct chunk_encoding {
chunk_encoding() { utils::zero_object(*this); };
// Index into g_chunk_encodings.
uint8 m_encoding_index;
// Number of tiles, endpoint indices.
uint8 m_num_tiles;
// Color, alpha0, alpha1
enum { cColorIndex = 0,
cAlpha0Index = 1,
cAlpha1Index = 2 };
uint16 m_endpoint_indices[3][cChunkMaxTiles];
uint16 m_selector_indices[3][cChunkBlockHeight][cChunkBlockWidth]; // [block_y][block_x]
};
typedef crnlib::vector<chunk_encoding> chunk_encoding_vec;
inline const chunk_encoding& get_chunk_encoding(uint chunk_index) const { return m_chunk_encoding[chunk_index]; }
inline const chunk_encoding_vec& get_chunk_encoding_vec() const { return m_chunk_encoding; }
struct selectors {
selectors() { utils::zero_object(*this); }
@@ -201,16 +206,12 @@ class dxt_hc {
const pixel_chunk_vec& get_compressed_chunk_pixels_quantized_alpha_selectors() const { return m_dbg_chunk_pixels_quantized_alpha_selectors; }
const pixel_chunk_vec& get_compressed_chunk_pixels_final_alpha_selectors() const { return m_dbg_chunk_pixels_final_alpha_selectors; }
static void create_debug_image_from_chunks(uint num_chunks_x, uint num_chunks_y, const pixel_chunk_vec& chunks, const chunk_encoding_vec* pChunk_encodings, image_u8& img, bool serpentine_scan, int comp_index = -1);
private:
params m_params;
uint m_num_chunks;
const pixel_chunk* m_pChunks;
chunk_encoding_vec m_chunk_encoding;
uint m_num_alpha_blocks; // 0, 1, or 2
bool m_has_color_blocks;
bool m_has_alpha0_blocks;
@@ -416,13 +417,12 @@ class dxt_hc {
bool refine_quantized_alpha_endpoints();
bool refine_quantized_alpha_selectors();
void create_final_debug_image();
bool create_chunk_encodings();
bool create_block_encodings(const params& p);
bool update_progress(uint phase_index, uint subphase_index, uint subphase_total);
bool compress_internal(const params& p, uint num_chunks, const pixel_chunk* pChunks);
};
CRNLIB_DEFINE_BITWISE_COPYABLE(dxt_hc::pixel_chunk);
CRNLIB_DEFINE_BITWISE_COPYABLE(dxt_hc::chunk_encoding);
CRNLIB_DEFINE_BITWISE_COPYABLE(dxt_hc::selectors);
} // namespace crnlib