mirror of
https://github.com/BinomialLLC/basis_universal.git
synced 2026-06-08 00:23:52 +00:00
Still a WIP - needs more testing (especially when video is disabled, which hasn't been tested on this branch yet):
- Adding conditional replenishment (CR) support to the system, so blocks which don't change their selectors or endpoints from the last frame don't need to be decoded. This can greatly increase the compression ratio on many video sequences. - Updating help text. - Adding support for slices to be marked as i-frames - Updating transcoder to support CR
This commit is contained in:
@@ -170,14 +170,13 @@ namespace basisu
|
||||
{
|
||||
{ -1, 0 },
|
||||
{ 0, -1 },
|
||||
{ -1, -1 }
|
||||
{ -1, -1 } // or conditional replenishment in videos
|
||||
};
|
||||
const uint32_t NUM_ENDPOINT_PREDS = BASISU_ARRAY_SIZE(g_endpoint_preds);
|
||||
const uint32_t NO_ENDPOINT_PRED_INDEX = 3;//NUM_ENDPOINT_PREDS;
|
||||
|
||||
|
||||
void basisu_backend::reoptimize_and_sort_endpoints_codebook(uint32_t total_block_endpoints_remapped, uint_vec &all_endpoint_indices)
|
||||
{
|
||||
basisu_frontend &r = *m_pFront_end;
|
||||
const bool is_video = r.get_params().m_tex_type == basist::cBASISTexTypeVideoFrames;
|
||||
|
||||
if (total_block_endpoints_remapped)
|
||||
{
|
||||
@@ -292,10 +291,106 @@ namespace basisu
|
||||
for (uint32_t i = 0; i < m_selector_remap_table_new_to_old.size(); i++)
|
||||
m_selector_remap_table_old_to_new[m_selector_remap_table_new_to_old[i]] = i;
|
||||
}
|
||||
|
||||
int basisu_backend::find_video_frame(int slice_index, int delta)
|
||||
{
|
||||
for (uint32_t s = 0; s < m_slices.size(); s++)
|
||||
{
|
||||
if ((int)m_slices[s].m_source_file_index != (m_slices[slice_index].m_source_file_index + delta))
|
||||
continue;
|
||||
|
||||
if (m_slices[s].m_mip_index != m_slices[slice_index].m_mip_index)
|
||||
continue;
|
||||
|
||||
// Being super paranoid here.
|
||||
if (m_slices[s].m_num_blocks_x != (m_slices[slice_index].m_num_blocks_x))
|
||||
continue;
|
||||
if (m_slices[s].m_num_blocks_y != (m_slices[slice_index].m_num_blocks_y))
|
||||
continue;
|
||||
if (m_slices[s].m_alpha != (m_slices[slice_index].m_alpha))
|
||||
continue;
|
||||
return s;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void basisu_backend::check_for_valid_cr_blocks()
|
||||
{
|
||||
basisu_frontend& r = *m_pFront_end;
|
||||
const bool is_video = r.get_params().m_tex_type == basist::cBASISTexTypeVideoFrames;
|
||||
|
||||
if (!is_video)
|
||||
return;
|
||||
|
||||
uint32_t total_crs = 0;
|
||||
uint32_t total_invalid_crs = 0;
|
||||
|
||||
for (uint32_t slice_index = 0; slice_index < m_slices.size(); slice_index++)
|
||||
{
|
||||
const bool is_iframe = m_slices[slice_index].m_iframe;
|
||||
const uint32_t first_block_index = m_slices[slice_index].m_first_block_index;
|
||||
|
||||
const uint32_t width = m_slices[slice_index].m_width;
|
||||
const uint32_t height = m_slices[slice_index].m_height;
|
||||
const uint32_t num_blocks_x = m_slices[slice_index].m_num_blocks_x;
|
||||
const uint32_t num_blocks_y = m_slices[slice_index].m_num_blocks_y;
|
||||
const int prev_frame_slice_index = find_video_frame(slice_index, -1);
|
||||
|
||||
// If we don't have a previous frame, and we're not an i-frame, something is wrong.
|
||||
if ((prev_frame_slice_index < 0) && (!is_iframe))
|
||||
{
|
||||
BASISU_BACKEND_VERIFY(0);
|
||||
}
|
||||
|
||||
if ((is_iframe) || (prev_frame_slice_index < 0))
|
||||
{
|
||||
// Ensure no blocks use CR's
|
||||
for (uint32_t block_y = 0; block_y < num_blocks_y; block_y++)
|
||||
{
|
||||
for (uint32_t block_x = 0; block_x < num_blocks_x; block_x++)
|
||||
{
|
||||
encoder_block& m = m_slice_encoder_blocks[slice_index](block_x, block_y);
|
||||
BASISU_BACKEND_VERIFY(m.m_endpoint_predictor != basist::CR_ENDPOINT_PRED_INDEX);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// For blocks that use CR's, make sure the endpoints/selectors haven't really changed.
|
||||
for (uint32_t block_y = 0; block_y < num_blocks_y; block_y++)
|
||||
{
|
||||
for (uint32_t block_x = 0; block_x < num_blocks_x; block_x++)
|
||||
{
|
||||
encoder_block& m = m_slice_encoder_blocks[slice_index](block_x, block_y);
|
||||
|
||||
if (m.m_endpoint_predictor == basist::CR_ENDPOINT_PRED_INDEX)
|
||||
{
|
||||
total_crs++;
|
||||
|
||||
encoder_block& prev_m = m_slice_encoder_blocks[prev_frame_slice_index](block_x, block_y);
|
||||
|
||||
if ((m.m_endpoint_index != prev_m.m_endpoint_index) || (m.m_selector_index != prev_m.m_selector_index))
|
||||
{
|
||||
total_invalid_crs++;
|
||||
}
|
||||
}
|
||||
} // block_x
|
||||
} // block_y
|
||||
|
||||
} // !slice_index
|
||||
|
||||
} // slice_index
|
||||
|
||||
debug_printf("Total CR's: %u, Total invalid CR's: %u\n", total_crs, total_invalid_crs);
|
||||
|
||||
BASISU_BACKEND_VERIFY(total_invalid_crs == 0);
|
||||
}
|
||||
|
||||
void basisu_backend::create_encoder_blocks()
|
||||
{
|
||||
basisu_frontend &r = *m_pFront_end;
|
||||
const bool is_video = r.get_params().m_tex_type == basist::cBASISTexTypeVideoFrames;
|
||||
|
||||
m_slice_encoder_blocks.resize(m_slices.size());
|
||||
|
||||
@@ -306,6 +401,9 @@ namespace basisu
|
||||
|
||||
for (uint32_t slice_index = 0; slice_index < m_slices.size(); slice_index++)
|
||||
{
|
||||
const int prev_frame_slice_index = is_video ? find_video_frame(slice_index, -1) : -1;
|
||||
const bool is_iframe = m_slices[slice_index].m_iframe;
|
||||
|
||||
const uint32_t first_block_index = m_slices[slice_index].m_first_block_index;
|
||||
|
||||
const uint32_t width = m_slices[slice_index].m_width;
|
||||
@@ -328,29 +426,50 @@ namespace basisu
|
||||
|
||||
m.m_selector_index = r.get_block_selector_cluster_index(block_index);
|
||||
|
||||
m.m_endpoint_predictor = NO_ENDPOINT_PRED_INDEX;
|
||||
m.m_endpoint_predictor = basist::NO_ENDPOINT_PRED_INDEX;
|
||||
|
||||
const uint32_t block_endpoint = m.m_endpoint_index;
|
||||
|
||||
uint32_t best_endpoint_pred = UINT32_MAX;
|
||||
|
||||
for (uint32_t endpoint_pred = 0; endpoint_pred < NUM_ENDPOINT_PREDS; endpoint_pred++)
|
||||
|
||||
for (uint32_t endpoint_pred = 0; endpoint_pred < basist::NUM_ENDPOINT_PREDS; endpoint_pred++)
|
||||
{
|
||||
int pred_block_x = block_x + g_endpoint_preds[endpoint_pred].m_dx;
|
||||
if ((pred_block_x < 0) || (pred_block_x >= (int)num_blocks_x))
|
||||
continue;
|
||||
|
||||
int pred_block_y = block_y + g_endpoint_preds[endpoint_pred].m_dy;
|
||||
if ((pred_block_y < 0) || (pred_block_y >= (int)num_blocks_y))
|
||||
continue;
|
||||
|
||||
uint32_t pred_endpoint = m_slice_encoder_blocks[slice_index](pred_block_x, pred_block_y).m_endpoint_index;
|
||||
|
||||
if (pred_endpoint == block_endpoint)
|
||||
if ((is_video) && (endpoint_pred == basist::CR_ENDPOINT_PRED_INDEX))
|
||||
{
|
||||
if (endpoint_pred < best_endpoint_pred)
|
||||
if ((prev_frame_slice_index != -1) && (!is_iframe))
|
||||
{
|
||||
best_endpoint_pred = endpoint_pred;
|
||||
const uint32_t cur_endpoint = m_slice_encoder_blocks[slice_index](block_x, block_y).m_endpoint_index;
|
||||
const uint32_t cur_selector = m_slice_encoder_blocks[slice_index](block_x, block_y).m_selector_index;
|
||||
|
||||
const uint32_t prev_endpoint = m_slice_encoder_blocks[prev_frame_slice_index](block_x, block_y).m_endpoint_index;
|
||||
const uint32_t prev_selector = m_slice_encoder_blocks[prev_frame_slice_index](block_x, block_y).m_selector_index;
|
||||
|
||||
if ((cur_endpoint == prev_endpoint) && (cur_selector == prev_selector))
|
||||
{
|
||||
best_endpoint_pred = basist::CR_ENDPOINT_PRED_INDEX;
|
||||
|
||||
m_slice_encoder_blocks[prev_frame_slice_index](block_x, block_y).m_is_cr_target = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int pred_block_x = block_x + g_endpoint_preds[endpoint_pred].m_dx;
|
||||
if ((pred_block_x < 0) || (pred_block_x >= (int)num_blocks_x))
|
||||
continue;
|
||||
|
||||
int pred_block_y = block_y + g_endpoint_preds[endpoint_pred].m_dy;
|
||||
if ((pred_block_y < 0) || (pred_block_y >= (int)num_blocks_y))
|
||||
continue;
|
||||
|
||||
uint32_t pred_endpoint = m_slice_encoder_blocks[slice_index](pred_block_x, pred_block_y).m_endpoint_index;
|
||||
|
||||
if (pred_endpoint == block_endpoint)
|
||||
{
|
||||
if (endpoint_pred < best_endpoint_pred)
|
||||
{
|
||||
best_endpoint_pred = endpoint_pred;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -381,8 +500,11 @@ namespace basisu
|
||||
|
||||
best_endpoint_pred = UINT32_MAX;
|
||||
|
||||
for (uint32_t endpoint_pred = 0; endpoint_pred < NUM_ENDPOINT_PREDS; endpoint_pred++)
|
||||
for (uint32_t endpoint_pred = 0; endpoint_pred < basist::NUM_ENDPOINT_PREDS; endpoint_pred++)
|
||||
{
|
||||
if ((is_video) && (endpoint_pred == basist::CR_ENDPOINT_PRED_INDEX))
|
||||
continue;
|
||||
|
||||
int pred_block_x = block_x + g_endpoint_preds[endpoint_pred].m_dx;
|
||||
if ((pred_block_x < 0) || (pred_block_x >= (int)num_blocks_x))
|
||||
continue;
|
||||
@@ -441,7 +563,7 @@ namespace basisu
|
||||
total_endpoint_pred_missed++;
|
||||
}
|
||||
|
||||
if (m.m_endpoint_predictor == NO_ENDPOINT_PRED_INDEX)
|
||||
if (m.m_endpoint_predictor == basist::NO_ENDPOINT_PRED_INDEX)
|
||||
{
|
||||
all_endpoint_indices.push_back(m.m_endpoint_index);
|
||||
}
|
||||
@@ -456,10 +578,12 @@ namespace basisu
|
||||
total_endpoint_pred_missed, total_endpoint_pred_missed * 100.0f / get_total_blocks(),
|
||||
total_endpoint_pred_hits, total_endpoint_pred_hits * 100.0f / get_total_blocks(),
|
||||
total_block_endpoints_remapped, total_block_endpoints_remapped * 100.0f / get_total_blocks());
|
||||
|
||||
|
||||
reoptimize_and_sort_endpoints_codebook(total_block_endpoints_remapped, all_endpoint_indices);
|
||||
|
||||
sort_selector_codebook();
|
||||
|
||||
check_for_valid_cr_blocks();
|
||||
}
|
||||
|
||||
void basisu_backend::compute_slice_crcs()
|
||||
@@ -481,22 +605,22 @@ namespace basisu
|
||||
{
|
||||
const uint32_t block_index = first_block_index + block_x + block_y * num_blocks_x;
|
||||
|
||||
encoder_block &m = m_slice_encoder_blocks[slice_index](block_x, block_y);
|
||||
encoder_block& m = m_slice_encoder_blocks[slice_index](block_x, block_y);
|
||||
|
||||
{
|
||||
etc_block &output_block = *(etc_block *)gi.get_block_ptr(block_x, block_y);
|
||||
etc_block& output_block = *(etc_block*)gi.get_block_ptr(block_x, block_y);
|
||||
|
||||
output_block.set_diff_bit(true);
|
||||
output_block.set_flip_bit(true);
|
||||
|
||||
|
||||
const uint32_t endpoint_index = m.m_endpoint_index;
|
||||
|
||||
|
||||
output_block.set_block_color5_etc1s(m_endpoint_palette[endpoint_index].m_color5);
|
||||
output_block.set_inten_tables_etc1s(m_endpoint_palette[endpoint_index].m_inten5);
|
||||
|
||||
const uint32_t selector_idx = m.m_selector_index;
|
||||
|
||||
const basist::etc1_selector_palette_entry &selectors = m_selector_palette[selector_idx];
|
||||
const basist::etc1_selector_palette_entry& selectors = m_selector_palette[selector_idx];
|
||||
for (uint32_t sy = 0; sy < 4; sy++)
|
||||
for (uint32_t sx = 0; sx < 4; sx++)
|
||||
output_block.set_selector(sx, sy, selectors(sx, sy));
|
||||
@@ -520,7 +644,7 @@ namespace basisu
|
||||
#endif
|
||||
save_png(buf, gi_unpacked);
|
||||
}
|
||||
|
||||
|
||||
} // slice_index
|
||||
}
|
||||
|
||||
@@ -528,6 +652,7 @@ namespace basisu
|
||||
bool basisu_backend::encode_image()
|
||||
{
|
||||
basisu_frontend &r = *m_pFront_end;
|
||||
const bool is_video = r.get_params().m_tex_type == basist::cBASISTexTypeVideoFrames;
|
||||
|
||||
uint32_t total_used_selector_history_buf = 0;
|
||||
uint32_t total_selector_indices_remapped = 0;
|
||||
@@ -555,6 +680,9 @@ namespace basisu
|
||||
|
||||
for (uint32_t slice_index = 0; slice_index < m_slices.size(); slice_index++)
|
||||
{
|
||||
const int prev_frame_slice_index = is_video ? find_video_frame(slice_index, -1) : -1;
|
||||
const int next_frame_slice_index = is_video ? find_video_frame(slice_index, 1) : -1;
|
||||
|
||||
const uint32_t first_block_index = m_slices[slice_index].m_first_block_index;
|
||||
const uint32_t width = m_slices[slice_index].m_width;
|
||||
const uint32_t height = m_slices[slice_index].m_height;
|
||||
@@ -584,10 +712,21 @@ namespace basisu
|
||||
else if (m.m_endpoint_predictor == 1)
|
||||
block_endpoints_are_referenced(block_x, block_y - 1) = true;
|
||||
else if (m.m_endpoint_predictor == 2)
|
||||
block_endpoints_are_referenced(block_x - 1, block_y - 1) = true;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
if (!is_video)
|
||||
block_endpoints_are_referenced(block_x - 1, block_y - 1) = true;
|
||||
}
|
||||
|
||||
if (is_video)
|
||||
{
|
||||
if (m.m_is_cr_target)
|
||||
block_endpoints_are_referenced(block_x, block_y) = true;
|
||||
}
|
||||
|
||||
} // block_x
|
||||
|
||||
} // block_y
|
||||
|
||||
for (uint32_t block_y = 0; block_y < num_blocks_y; block_y++)
|
||||
{
|
||||
for (uint32_t block_x = 0; block_x < num_blocks_x; block_x++)
|
||||
@@ -607,7 +746,7 @@ namespace basisu
|
||||
const uint32_t bx = block_x + x;
|
||||
const uint32_t by = block_y + y;
|
||||
|
||||
uint32_t pred = NO_ENDPOINT_PRED_INDEX;
|
||||
uint32_t pred = basist::NO_ENDPOINT_PRED_INDEX;
|
||||
if ((bx < num_blocks_x) && (by < num_blocks_y))
|
||||
pred = m_slice_encoder_blocks[slice_index](bx, by).m_endpoint_predictor;
|
||||
|
||||
@@ -647,11 +786,11 @@ namespace basisu
|
||||
|
||||
prev_endpoint_pred_sym_bits = endpoint_pred_cur_sym_bits;
|
||||
}
|
||||
}
|
||||
} // if (((block_x & 1) == 0) && ((block_y & 1) == 0))
|
||||
|
||||
int new_endpoint_index = m_endpoint_remap_table_old_to_new[m.m_endpoint_index];
|
||||
|
||||
if (m.m_endpoint_predictor == NO_ENDPOINT_PRED_INDEX)
|
||||
if (m.m_endpoint_predictor == basist::NO_ENDPOINT_PRED_INDEX)
|
||||
{
|
||||
int endpoint_delta = new_endpoint_index - prev_endpoint_index;
|
||||
|
||||
@@ -719,17 +858,34 @@ namespace basisu
|
||||
endpoint_delta += (int)r.get_total_endpoint_clusters();
|
||||
|
||||
delta_endpoint_histogram.inc(endpoint_delta);
|
||||
}
|
||||
} // if (m.m_endpoint_predictor == NO_ENDPOINT_PRED_INDEX)
|
||||
|
||||
block_endpoint_indices.push_back(m_endpoint_remap_table_new_to_old[new_endpoint_index]);
|
||||
|
||||
prev_endpoint_index = new_endpoint_index;
|
||||
|
||||
if ((!is_video) || (m.m_endpoint_predictor != basist::CR_ENDPOINT_PRED_INDEX))
|
||||
{
|
||||
int new_selector_index = m_selector_remap_table_old_to_new[m.m_selector_index];
|
||||
|
||||
int selector_history_buf_index = -1;
|
||||
|
||||
if (m.m_is_cr_target)
|
||||
{
|
||||
for (uint32_t j = 0; j < selector_history_buf.size(); j++)
|
||||
{
|
||||
const int trial_idx = selector_history_buf[j];
|
||||
if (trial_idx == new_selector_index)
|
||||
{
|
||||
total_used_selector_history_buf++;
|
||||
selector_history_buf_index = j;
|
||||
selector_history_buf_histogram.inc(j);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const pixel_block &src_pixels = r.get_source_pixel_block(block_index);
|
||||
|
||||
@@ -796,11 +952,9 @@ namespace basisu
|
||||
selector_history_buf_histogram.inc(best_trial_history_buf_idx);
|
||||
}
|
||||
} // if (m_params.m_selector_rdo_quality_thresh > 0.0f)
|
||||
|
||||
|
||||
m.m_selector_index = m_selector_remap_table_new_to_old[new_selector_index];
|
||||
|
||||
block_selector_indices.push_back(m.m_selector_index);
|
||||
|
||||
if ((selector_history_buf_rle_count) && (selector_history_buf_index != 0))
|
||||
{
|
||||
if (selector_history_buf_rle_count >= (int)basist::SELECTOR_HISTORY_BUF_RLE_COUNT_THRESH)
|
||||
@@ -857,7 +1011,10 @@ namespace basisu
|
||||
selector_history_buf.add(new_selector_index);
|
||||
else if (selector_history_buf.size())
|
||||
selector_history_buf.use(selector_history_buf_index);
|
||||
}
|
||||
|
||||
} // if ((!is_video) || (m.m_endpoint_predictor != basist::CR_ENDPOINT_PRED_INDEX))
|
||||
|
||||
block_selector_indices.push_back(m.m_selector_index);
|
||||
|
||||
} // block_x
|
||||
|
||||
@@ -931,6 +1088,8 @@ namespace basisu
|
||||
create_endpoint_palette();
|
||||
}
|
||||
|
||||
check_for_valid_cr_blocks();
|
||||
|
||||
compute_slice_crcs();
|
||||
|
||||
double endpoint_pred_entropy = endpoint_pred_histogram.get_entropy() / endpoint_pred_histogram.get_total();
|
||||
@@ -939,6 +1098,9 @@ namespace basisu
|
||||
|
||||
debug_printf("Histogram entropy: EndpointPred: %3.3f DeltaEndpoint: %3.3f DeltaSelector: %3.3f\n", endpoint_pred_entropy, delta_endpoint_entropy, selector_entropy);
|
||||
|
||||
if (!endpoint_pred_histogram.get_total())
|
||||
endpoint_pred_histogram.inc(0);
|
||||
|
||||
huffman_encoding_table endpoint_pred_model;
|
||||
if (!endpoint_pred_model.init(endpoint_pred_histogram, 16))
|
||||
{
|
||||
@@ -946,6 +1108,9 @@ namespace basisu
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!delta_endpoint_histogram.get_total())
|
||||
delta_endpoint_histogram.inc(0);
|
||||
|
||||
huffman_encoding_table delta_endpoint_model;
|
||||
if (!delta_endpoint_model.init(delta_endpoint_histogram, 16))
|
||||
{
|
||||
@@ -953,6 +1118,9 @@ namespace basisu
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!selector_histogram.get_total())
|
||||
selector_histogram.inc(0);
|
||||
|
||||
huffman_encoding_table selector_model;
|
||||
if (!selector_model.init(selector_histogram, 16))
|
||||
{
|
||||
@@ -1047,11 +1215,11 @@ namespace basisu
|
||||
prev_endpoint_pred_sym = sym;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // if (((block_x & 1) == 0) && ((block_y & 1) == 0))
|
||||
|
||||
const int new_endpoint_index = m_endpoint_remap_table_old_to_new[m.m_endpoint_index];
|
||||
|
||||
if (m.m_endpoint_predictor == NO_ENDPOINT_PRED_INDEX)
|
||||
if (m.m_endpoint_predictor == basist::NO_ENDPOINT_PRED_INDEX)
|
||||
{
|
||||
int endpoint_delta = new_endpoint_index - prev_endpoint_index;
|
||||
if (endpoint_delta < 0)
|
||||
@@ -1062,32 +1230,35 @@ namespace basisu
|
||||
|
||||
prev_endpoint_index = new_endpoint_index;
|
||||
|
||||
if (!selector_rle_count)
|
||||
if ((!is_video) || (m.m_endpoint_predictor != basist::CR_ENDPOINT_PRED_INDEX))
|
||||
{
|
||||
uint32_t selector_sym_index = selector_syms[slice_index][cur_selector_sym_ofs++];
|
||||
|
||||
if (selector_sym_index == SELECTOR_HISTORY_BUF_RLE_SYMBOL_INDEX)
|
||||
selector_rle_count = selector_syms[slice_index][cur_selector_sym_ofs++];
|
||||
|
||||
total_selector_bits += coder.put_code(selector_sym_index, selector_model);
|
||||
|
||||
if (selector_sym_index == SELECTOR_HISTORY_BUF_RLE_SYMBOL_INDEX)
|
||||
if (!selector_rle_count)
|
||||
{
|
||||
int run_sym = selector_rle_count - basist::SELECTOR_HISTORY_BUF_RLE_COUNT_THRESH;
|
||||
if (run_sym >= ((int)basist::SELECTOR_HISTORY_BUF_RLE_COUNT_TOTAL - 1))
|
||||
{
|
||||
total_selector_bits += coder.put_code(basist::SELECTOR_HISTORY_BUF_RLE_COUNT_TOTAL - 1, selector_history_buf_rle_model);
|
||||
|
||||
uint32_t n = selector_rle_count - basist::SELECTOR_HISTORY_BUF_RLE_COUNT_THRESH;
|
||||
total_selector_bits += coder.put_vlc(n, 7);
|
||||
}
|
||||
else
|
||||
total_selector_bits += coder.put_code(run_sym, selector_history_buf_rle_model);
|
||||
}
|
||||
}
|
||||
uint32_t selector_sym_index = selector_syms[slice_index][cur_selector_sym_ofs++];
|
||||
|
||||
if (selector_rle_count)
|
||||
selector_rle_count--;
|
||||
if (selector_sym_index == SELECTOR_HISTORY_BUF_RLE_SYMBOL_INDEX)
|
||||
selector_rle_count = selector_syms[slice_index][cur_selector_sym_ofs++];
|
||||
|
||||
total_selector_bits += coder.put_code(selector_sym_index, selector_model);
|
||||
|
||||
if (selector_sym_index == SELECTOR_HISTORY_BUF_RLE_SYMBOL_INDEX)
|
||||
{
|
||||
int run_sym = selector_rle_count - basist::SELECTOR_HISTORY_BUF_RLE_COUNT_THRESH;
|
||||
if (run_sym >= ((int)basist::SELECTOR_HISTORY_BUF_RLE_COUNT_TOTAL - 1))
|
||||
{
|
||||
total_selector_bits += coder.put_code(basist::SELECTOR_HISTORY_BUF_RLE_COUNT_TOTAL - 1, selector_history_buf_rle_model);
|
||||
|
||||
uint32_t n = selector_rle_count - basist::SELECTOR_HISTORY_BUF_RLE_COUNT_THRESH;
|
||||
total_selector_bits += coder.put_vlc(n, 7);
|
||||
}
|
||||
else
|
||||
total_selector_bits += coder.put_code(run_sym, selector_history_buf_rle_model);
|
||||
}
|
||||
}
|
||||
|
||||
if (selector_rle_count)
|
||||
selector_rle_count--;
|
||||
}
|
||||
|
||||
} // block_x
|
||||
|
||||
@@ -1471,9 +1642,11 @@ namespace basisu
|
||||
|
||||
uint32_t basisu_backend::encode()
|
||||
{
|
||||
const bool is_video = m_pFront_end->get_params().m_tex_type == basist::cBASISTexTypeVideoFrames;
|
||||
|
||||
m_output.m_slice_desc = m_slices;
|
||||
m_output.m_etc1s = m_params.m_etc1s;
|
||||
|
||||
|
||||
create_endpoint_palette();
|
||||
create_selector_palette();
|
||||
|
||||
|
||||
@@ -36,6 +36,8 @@ namespace basisu
|
||||
|
||||
int m_selector_history_buf_index;
|
||||
|
||||
bool m_is_cr_target;
|
||||
|
||||
void clear()
|
||||
{
|
||||
m_endpoint_predictor = 0;
|
||||
@@ -44,6 +46,8 @@ namespace basisu
|
||||
m_selector_index = 0;
|
||||
|
||||
m_selector_history_buf_index = 0;
|
||||
|
||||
m_is_cr_target = false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -103,6 +107,16 @@ namespace basisu
|
||||
|
||||
struct basisu_backend_slice_desc
|
||||
{
|
||||
basisu_backend_slice_desc()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
clear_obj(*this);
|
||||
}
|
||||
|
||||
uint32_t m_first_block_index;
|
||||
|
||||
uint32_t m_orig_width;
|
||||
@@ -119,7 +133,9 @@ namespace basisu
|
||||
|
||||
uint32_t m_source_file_index; // also the basis image index
|
||||
uint32_t m_mip_index;
|
||||
|
||||
bool m_alpha;
|
||||
bool m_iframe;
|
||||
};
|
||||
|
||||
typedef std::vector<basisu_backend_slice_desc> basisu_backend_slice_desc_vec;
|
||||
@@ -306,6 +322,8 @@ namespace basisu
|
||||
bool encode_image();
|
||||
bool encode_endpoint_palette();
|
||||
bool encode_selector_palette();
|
||||
int find_video_frame(int slice_index, int delta);
|
||||
void check_for_valid_cr_blocks();
|
||||
};
|
||||
|
||||
} // namespace basisu
|
||||
|
||||
@@ -86,6 +86,9 @@ namespace basisu
|
||||
|
||||
if (slice_descs[i].m_alpha)
|
||||
m_images_descs[i].m_flags = m_images_descs[i].m_flags | basist::cSliceDescFlagsIsAlphaData;
|
||||
|
||||
if (slice_descs[i].m_iframe)
|
||||
m_images_descs[i].m_flags = m_images_descs[i].m_flags | basist::cSliceDescFlagsFrameIsIFrame;
|
||||
|
||||
m_images_descs[i].m_orig_width = slice_descs[i].m_orig_width;
|
||||
m_images_descs[i].m_orig_height = slice_descs[i].m_orig_height;
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
#include <unordered_set>
|
||||
|
||||
#define BASISU_USE_STB_IMAGE_RESIZE_FOR_MIPMAP_GEN 0
|
||||
#define DEBUG_RESIZE_TEXTURE_TO_64x64 (0)
|
||||
#define DEBUG_CROP_TEXTURE_TO_64x64 (0)
|
||||
#define DEBUG_RESIZE_TEXTURE (0)
|
||||
#define DEBUG_EXTRACT_SINGLE_BLOCK (0)
|
||||
|
||||
namespace basisu
|
||||
@@ -301,8 +302,6 @@ namespace basisu
|
||||
|
||||
if (has_alpha)
|
||||
m_any_source_image_has_alpha = true;
|
||||
|
||||
debug_printf("Source image index %u filename %s %ux%u has alpha: %u\n", source_file_index, pSource_filename, file_image.get_width(), file_image.get_height(), has_alpha);
|
||||
|
||||
if (m_params.m_y_flip)
|
||||
file_image.flip_y();
|
||||
@@ -315,10 +314,18 @@ namespace basisu
|
||||
file_image = block_image;
|
||||
#endif
|
||||
|
||||
#if DEBUG_RESIZE_TEXTURE_TO_64x64
|
||||
#if DEBUG_CROP_TEXTURE_TO_64x64
|
||||
file_image.resize(64, 64);
|
||||
#endif
|
||||
|
||||
#if DEBUG_RESIZE_TEXTURE
|
||||
image temp_img((file_image.get_width() + 3) / 4, (file_image.get_height() + 3) / 4);
|
||||
image_resample(file_image, temp_img, m_params.m_perceptual, "kaiser");
|
||||
temp_img.swap(file_image);
|
||||
#endif
|
||||
|
||||
debug_printf("Source image index %u filename %s %ux%u has alpha: %u\n", source_file_index, pSource_filename, file_image.get_width(), file_image.get_height(), has_alpha);
|
||||
|
||||
if ((!file_image.get_width()) || (!file_image.get_height()))
|
||||
{
|
||||
error_printf("basis_compressor::read_source_images: Source image has a zero width and/or height!\n");
|
||||
@@ -445,6 +452,15 @@ namespace basisu
|
||||
slice_desc.m_mip_index = mip_indices[slice_index];
|
||||
|
||||
slice_desc.m_alpha = is_alpha_slice;
|
||||
|
||||
slice_desc.m_iframe = false;
|
||||
|
||||
if (m_params.m_tex_type == basist::cBASISTexTypeVideoFrames)
|
||||
{
|
||||
// TODO: For now, only the first frame is an i-frame in videos.
|
||||
// We should allow the caller to specify the i-frame frequency, for fast seeking.
|
||||
slice_desc.m_iframe = (source_file_index == 0);
|
||||
}
|
||||
|
||||
m_total_blocks += slice_desc.m_num_blocks_x * slice_desc.m_num_blocks_y;
|
||||
total_macroblocks += slice_desc.m_num_macroblocks_x * slice_desc.m_num_macroblocks_y;
|
||||
@@ -488,8 +504,8 @@ namespace basisu
|
||||
{
|
||||
const basisu_backend_slice_desc &slice_desc = m_slice_descs[i];
|
||||
|
||||
printf("Slice: %u, alpha: %u, orig width/height: %ux%u, width/height: %ux%u, first_block: %u, image_index: %u, mip_level: %u\n",
|
||||
i, slice_desc.m_alpha, slice_desc.m_orig_width, slice_desc.m_orig_height, slice_desc.m_width, slice_desc.m_height, slice_desc.m_first_block_index, slice_desc.m_source_file_index, slice_desc.m_mip_index);
|
||||
printf("Slice: %u, alpha: %u, orig width/height: %ux%u, width/height: %ux%u, first_block: %u, image_index: %u, mip_level: %u, iframe: %u\n",
|
||||
i, slice_desc.m_alpha, slice_desc.m_orig_width, slice_desc.m_orig_height, slice_desc.m_width, slice_desc.m_height, slice_desc.m_first_block_index, slice_desc.m_source_file_index, slice_desc.m_mip_index, slice_desc.m_iframe);
|
||||
|
||||
if (m_any_source_image_has_alpha)
|
||||
{
|
||||
@@ -523,6 +539,13 @@ namespace basisu
|
||||
|
||||
if ((slice_desc.m_orig_width > slice_desc.m_width) || (slice_desc.m_orig_height > slice_desc.m_height))
|
||||
return false;
|
||||
|
||||
if ((slice_desc.m_source_file_index == 0) && (m_params.m_tex_type == basist::cBASISTexTypeVideoFrames))
|
||||
{
|
||||
// First image in the basis video file must be an i-frame.
|
||||
if (!slice_desc.m_iframe)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -780,6 +803,7 @@ namespace basisu
|
||||
p.m_debug_stats = m_params.m_debug;
|
||||
p.m_debug_images = m_params.m_debug_images;
|
||||
p.m_faster = m_params.m_faster;
|
||||
p.m_tex_type = m_params.m_tex_type;
|
||||
|
||||
if ((m_params.m_global_sel_pal) || (m_auto_global_sel_pal))
|
||||
{
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "basisu_etc.h"
|
||||
#include "basisu_gpu_texture.h"
|
||||
#include "basisu_global_selector_palette_helpers.h"
|
||||
#include "transcoder/basisu_file_headers.h"
|
||||
|
||||
namespace basisu
|
||||
{
|
||||
@@ -70,7 +71,8 @@ namespace basisu
|
||||
m_num_global_sel_codebook_mod_bits(0),
|
||||
m_use_hybrid_selector_codebooks(false),
|
||||
m_hybrid_codebook_quality_thresh(0.0f),
|
||||
m_validate(false)
|
||||
m_validate(false),
|
||||
m_tex_type(basist::cBASISTexType2D)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -93,6 +95,8 @@ namespace basisu
|
||||
uint32_t m_num_global_sel_codebook_mod_bits;
|
||||
bool m_use_hybrid_selector_codebooks;
|
||||
float m_hybrid_codebook_quality_thresh;
|
||||
|
||||
basist::basis_texture_type m_tex_type;
|
||||
};
|
||||
|
||||
bool init(const params &p);
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
using namespace basisu;
|
||||
|
||||
#define BASISU_TOOL_VERSION "1.05.00"
|
||||
#define BASISU_TOOL_VERSION "1.06.00"
|
||||
|
||||
enum tool_mode
|
||||
{
|
||||
@@ -71,8 +71,9 @@ static void print_usage()
|
||||
" -stats: Compute and display image quality metrics (slightly slower).\n"
|
||||
" -slower: Enable optional stages in the compressor for slower but higher quality compression using better codebooks.\n"
|
||||
" -tex_type <2d, 2darray, 3d, video, cubemap>: Set Basis file header's texture type field. Cubemap arrays require multiples of 6 images, in X+, X-, Y+, Y-, Z+, Z- order, each image must be the same resolutions.\n"
|
||||
" 2d=arbitrary 2D images, 2darray=2D array, 3D=volume texture slices, video=video frames, cubemap=array of faces. For 2darray/3d/cubemaps/video, each source image's dimensions and # of mipmap levels must be the same.\n"
|
||||
" -framerate X: Set framerate in header to X/frames sec\n"
|
||||
" (2d=arbitrary 2D images, 2darray=2D array, 3D=volume texture slices, video=video frames, cubemap=array of faces. For 2darray/3d/cubemaps/video, each source image's dimensions and # of mipmap levels must be the same.)\n"
|
||||
" For video, the .basis file will be written with the first frame being an I-Frame, and subsequent frames being P-Frames (using conditional replenishment). Playback must always occur in order from first to last image.\n"
|
||||
" -framerate X: Set framerate in header to X/frames sec.\n"
|
||||
" -individual: Process input images individually and output multiple .basis files (not as a texture array)\n"
|
||||
" -fuzz_testing: Use with -validate: Disables CRC16 validation of file contents before transcoding\n"
|
||||
"\n"
|
||||
@@ -124,8 +125,12 @@ static void print_usage()
|
||||
"basisu -q 255 -file x.png -mipmap -debug -stats : Compress sRGB x.png to x.basis at quality level 255 with compressor debug output/statistics\n"
|
||||
"basisu -linear -max_endpoints 16128 -max_selectors 16128 -file x.png : Compress non-sRGB x.png to x.basis using the largest supported manually specified codebook sizes\n"
|
||||
"basisu -linear -global_sel_pal -no_hybrid_sel_cb -file x.png : Compress a non-sRGB image, use virtual selector codebooks for improved compression (but slower encoding)\n"
|
||||
"basisu -linear -global_sel_pal -file x.png: Compress a non-sRGB image, use hybrid selector codebooks for slightly improved compression (but slower encoding)\n"
|
||||
"basisu -tex_type video -framerate 20 -multifile_printf \"x%02u.png\" -multifile_first 1 -multifile_count 20 : Compress a 20 sRGB source image video sequence (x01.png, x02.png, x03.png, etc.) to x01.basis\n"
|
||||
"basisu -linear -global_sel_pal -file x.png : Compress a non-sRGB image, use hybrid selector codebooks for slightly improved compression (but slower encoding)\n"
|
||||
"basisu -tex_type video -framerate 20 -slower -multifile_printf \"x%02u.png\" -multifile_first 1 -multifile_count 99 : Compress a 99 frame sRGB source image video sequence (x01.png, x02.png, x03.png, etc.) to x01.basis\n"
|
||||
"\n"
|
||||
"Note: For video use, it's recommended you use a very powerful machine with many cores. Use -slower for better codebook generation, specify very large codebooks using -max_endpoints and -max_selectors, and reduce\n"
|
||||
"the default endpoint RDO threshold (-endpoint_rdo_thresh) to around 1.25. Videos may have mipmaps and alpha channels. Videos must always be played back by the transcoder in first to last image order.\n"
|
||||
"Video files currently use I-Frames on the first image, and P-Frames using conditional replenishment on subsequent frames.\n"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -876,20 +881,20 @@ static bool unpack_and_validate_mode(command_line_params &opts, bool validate_fl
|
||||
}
|
||||
|
||||
// Now transcode the file to all supported texture formats and save mipmapped KTX files
|
||||
for (uint32_t image_index = 0; image_index < fileinfo.m_total_images; image_index++)
|
||||
for (int format_iter = 0; format_iter < basist::cTFTotalTextureFormats; format_iter++)
|
||||
{
|
||||
for (uint32_t level_index = 0; level_index < fileinfo.m_image_mipmap_levels[image_index]; level_index++)
|
||||
for (uint32_t image_index = 0; image_index < fileinfo.m_total_images; image_index++)
|
||||
{
|
||||
basist::basisu_image_level_info level_info;
|
||||
|
||||
if (!dec.get_image_level_info(&basis_data[0], (uint32_t)basis_data.size(), level_info, image_index, level_index))
|
||||
for (uint32_t level_index = 0; level_index < fileinfo.m_image_mipmap_levels[image_index]; level_index++)
|
||||
{
|
||||
error_printf("Failed retrieving image level information (%u %u)!\n", image_index, level_index);
|
||||
return false;
|
||||
}
|
||||
basist::basisu_image_level_info level_info;
|
||||
|
||||
for (int format_iter = 0; format_iter < basist::cTFTotalTextureFormats; format_iter++)
|
||||
{
|
||||
if (!dec.get_image_level_info(&basis_data[0], (uint32_t)basis_data.size(), level_info, image_index, level_index))
|
||||
{
|
||||
error_printf("Failed retrieving image level information (%u %u)!\n", image_index, level_index);
|
||||
return false;
|
||||
}
|
||||
|
||||
const basist::transcoder_texture_format transcoder_tex_fmt = static_cast<basist::transcoder_texture_format>(format_iter);
|
||||
|
||||
if (transcoder_tex_fmt == basist::cTFPVRTC1_4_OPAQUE_ONLY)
|
||||
@@ -968,7 +973,7 @@ static bool unpack_and_validate_mode(command_line_params &opts, bool validate_fl
|
||||
|
||||
if ((!opts.m_no_ktx) && (fileinfo.m_tex_type != basist::cBASISTexTypeCubemapArray))
|
||||
{
|
||||
std::string ktx_filename(base_filename + string_format("_transcoded_%s_%u.ktx", basist::basis_get_format_name(transcoder_tex_fmt), image_index));
|
||||
std::string ktx_filename(base_filename + string_format("_transcoded_%s_%04u.ktx", basist::basis_get_format_name(transcoder_tex_fmt), image_index));
|
||||
if (!write_compressed_texture_file(ktx_filename.c_str(), gi))
|
||||
{
|
||||
error_printf("Failed writing KTX file \"%s\"!\n", ktx_filename.c_str());
|
||||
@@ -995,7 +1000,12 @@ static bool unpack_and_validate_mode(command_line_params &opts, bool validate_fl
|
||||
}
|
||||
//u.crop(level_info.m_orig_width, level_info.m_orig_height);
|
||||
|
||||
std::string rgb_filename(base_filename + string_format("_unpacked_rgb_%s_%u_%u.png", basist::basis_get_format_name(transcoder_tex_fmt), image_index, level_index));
|
||||
std::string rgb_filename;
|
||||
if (gi.size() > 1)
|
||||
rgb_filename = base_filename + string_format("_unpacked_rgb_%s_%u_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), level_index, image_index);
|
||||
else
|
||||
rgb_filename = base_filename + string_format("_unpacked_rgb_%s_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), image_index);
|
||||
|
||||
if (!save_png(rgb_filename, u, cImageSaveIgnoreAlpha))
|
||||
{
|
||||
error_printf("Failed writing to PNG file \"%s\"\n", rgb_filename.c_str());
|
||||
@@ -1005,7 +1015,12 @@ static bool unpack_and_validate_mode(command_line_params &opts, bool validate_fl
|
||||
|
||||
if (basis_transcoder_format_has_alpha(transcoder_tex_fmt))
|
||||
{
|
||||
std::string a_filename(base_filename + string_format("_unpacked_a_%s_%u_%u.png", basist::basis_get_format_name(transcoder_tex_fmt), image_index, level_index));
|
||||
std::string a_filename;
|
||||
if (gi.size() > 1)
|
||||
a_filename = base_filename + string_format("_unpacked_a_%s_%u_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), level_index, image_index);
|
||||
else
|
||||
a_filename = base_filename + string_format("_unpacked_a_%s_%04u.png", basist::basis_get_format_name(transcoder_tex_fmt), image_index);
|
||||
|
||||
if (!save_png(a_filename, u, cImageSaveGrayscale, 3))
|
||||
{
|
||||
error_printf("Failed writing to PNG file \"%s\"\n", a_filename.c_str());
|
||||
|
||||
@@ -20,7 +20,8 @@ namespace basist
|
||||
// Slice desc header flags
|
||||
enum basis_slice_desc_flags
|
||||
{
|
||||
cSliceDescFlagsIsAlphaData = 1,
|
||||
cSliceDescFlagsIsAlphaData = 1, // Slice has the image's alpha data
|
||||
cSliceDescFlagsFrameIsIFrame = 2 // Video only: Frame doesn't refer to previous frame (no usage of conditional replenishment pred symbols)
|
||||
};
|
||||
|
||||
#pragma pack(push)
|
||||
|
||||
@@ -3669,10 +3669,30 @@ namespace basist
|
||||
}
|
||||
|
||||
bool basisu_lowlevel_transcoder::transcode_slice(void *pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t *pImage_data, uint32_t image_data_size, block_format fmt,
|
||||
uint32_t output_stride, bool pvrtc_wrap_addressing, bool bc1_allow_threecolor_blocks)
|
||||
uint32_t output_stride, bool pvrtc_wrap_addressing, bool bc1_allow_threecolor_blocks, const basis_file_header &header, const basis_slice_desc &slice_desc)
|
||||
{
|
||||
const bool is_video = (header.m_tex_type == cBASISTexTypeVideoFrames);
|
||||
|
||||
const uint32_t total_blocks = num_blocks_x * num_blocks_y;
|
||||
|
||||
std::vector<uint32_t>* pPrevFrameIndices = nullptr;
|
||||
if (is_video)
|
||||
{
|
||||
// TODO: Add check to make sure the caller hasn't tried skipping past p-frames
|
||||
const bool alpha_flag = (slice_desc.m_flags & cSliceDescFlagsIsAlphaData) != 0;
|
||||
const uint32_t level_index = slice_desc.m_level_index;
|
||||
|
||||
if (level_index >= cMaxPrevFrameLevels)
|
||||
{
|
||||
assert(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
pPrevFrameIndices = &m_prev_frame_indices[alpha_flag][level_index];
|
||||
if (pPrevFrameIndices->size() < total_blocks)
|
||||
pPrevFrameIndices->resize(total_blocks);
|
||||
}
|
||||
|
||||
basist::bitwise_decoder sym_codec;
|
||||
|
||||
if (!sym_codec.init(pImage_data, image_data_size))
|
||||
@@ -3683,8 +3703,6 @@ namespace basist
|
||||
|
||||
approx_move_to_front selector_history_buf(m_selector_history_buf_size);
|
||||
|
||||
int prev_selector_index = 0;
|
||||
|
||||
const uint32_t SELECTOR_HISTORY_BUF_FIRST_SYMBOL_INDEX = (uint32_t)m_selectors.size();
|
||||
const uint32_t SELECTOR_HISTORY_BUF_RLE_SYMBOL_INDEX = m_selector_history_buf_size + SELECTOR_HISTORY_BUF_FIRST_SYMBOL_INDEX;
|
||||
uint32_t cur_selector_rle_count = 0;
|
||||
@@ -3759,7 +3777,7 @@ namespace basist
|
||||
}
|
||||
|
||||
// Decode endpoint index
|
||||
uint32_t endpoint_index;
|
||||
uint32_t endpoint_index, selector_index = 0;
|
||||
|
||||
const uint32_t pred = cur_pred_bits & 3;
|
||||
cur_pred_bits >>= 2;
|
||||
@@ -3792,16 +3810,29 @@ namespace basist
|
||||
}
|
||||
else if (pred == 2)
|
||||
{
|
||||
// Upper left
|
||||
if ((!block_x) || (!block_y))
|
||||
if (is_video)
|
||||
{
|
||||
BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: invalid datastream (2)\n");
|
||||
if (pPVRTC_work_mem)
|
||||
free(pPVRTC_work_mem);
|
||||
return false;
|
||||
}
|
||||
assert(pred == CR_ENDPOINT_PRED_INDEX);
|
||||
|
||||
endpoint_index = m_block_endpoint_preds[cur_block_endpoint_pred_array ^ 1][block_x - 1].m_endpoint_index;
|
||||
// Conditional replenishment
|
||||
endpoint_index = (*pPrevFrameIndices)[block_x + block_y * num_blocks_x];
|
||||
|
||||
selector_index = endpoint_index >> 16;
|
||||
endpoint_index &= 0xFFFFU;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Upper left
|
||||
if ((!block_x) || (!block_y))
|
||||
{
|
||||
BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: invalid datastream (2)\n");
|
||||
if (pPVRTC_work_mem)
|
||||
free(pPVRTC_work_mem);
|
||||
return false;
|
||||
}
|
||||
|
||||
endpoint_index = m_block_endpoint_preds[cur_block_endpoint_pred_array ^ 1][block_x - 1].m_endpoint_index;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3818,71 +3849,71 @@ namespace basist
|
||||
prev_endpoint_index = endpoint_index;
|
||||
|
||||
// Decode selector index
|
||||
uint32_t selector_index;
|
||||
int selector_sym;
|
||||
if (cur_selector_rle_count > 0)
|
||||
if ((!is_video) || (pred != CR_ENDPOINT_PRED_INDEX))
|
||||
{
|
||||
cur_selector_rle_count--;
|
||||
|
||||
selector_sym = (int)m_selectors.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
selector_sym = sym_codec.decode_huffman(m_selector_model);
|
||||
|
||||
if (selector_sym == static_cast<int>(SELECTOR_HISTORY_BUF_RLE_SYMBOL_INDEX))
|
||||
int selector_sym;
|
||||
if (cur_selector_rle_count > 0)
|
||||
{
|
||||
int run_sym = sym_codec.decode_huffman(m_selector_history_buf_rle_model);
|
||||
cur_selector_rle_count--;
|
||||
|
||||
if (run_sym == (SELECTOR_HISTORY_BUF_RLE_COUNT_TOTAL - 1))
|
||||
cur_selector_rle_count = sym_codec.decode_vlc(7) + SELECTOR_HISTORY_BUF_RLE_COUNT_THRESH;
|
||||
else
|
||||
cur_selector_rle_count = run_sym + SELECTOR_HISTORY_BUF_RLE_COUNT_THRESH;
|
||||
selector_sym = (int)m_selectors.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
selector_sym = sym_codec.decode_huffman(m_selector_model);
|
||||
|
||||
if (cur_selector_rle_count > total_blocks)
|
||||
if (selector_sym == static_cast<int>(SELECTOR_HISTORY_BUF_RLE_SYMBOL_INDEX))
|
||||
{
|
||||
int run_sym = sym_codec.decode_huffman(m_selector_history_buf_rle_model);
|
||||
|
||||
if (run_sym == (SELECTOR_HISTORY_BUF_RLE_COUNT_TOTAL - 1))
|
||||
cur_selector_rle_count = sym_codec.decode_vlc(7) + SELECTOR_HISTORY_BUF_RLE_COUNT_THRESH;
|
||||
else
|
||||
cur_selector_rle_count = run_sym + SELECTOR_HISTORY_BUF_RLE_COUNT_THRESH;
|
||||
|
||||
if (cur_selector_rle_count > total_blocks)
|
||||
{
|
||||
// The file is corrupted or we've got a bug.
|
||||
BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: invalid datastream (3)\n");
|
||||
if (pPVRTC_work_mem)
|
||||
free(pPVRTC_work_mem);
|
||||
return false;
|
||||
}
|
||||
|
||||
selector_sym = (int)m_selectors.size();
|
||||
|
||||
cur_selector_rle_count--;
|
||||
}
|
||||
}
|
||||
|
||||
if (selector_sym >= (int)m_selectors.size())
|
||||
{
|
||||
assert(m_selector_history_buf_size > 0);
|
||||
|
||||
int history_buf_index = selector_sym - (int)m_selectors.size();
|
||||
|
||||
if (history_buf_index >= (int)selector_history_buf.size())
|
||||
{
|
||||
// The file is corrupted or we've got a bug.
|
||||
BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: invalid datastream (3)\n");
|
||||
BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: invalid datastream (4)\n");
|
||||
if (pPVRTC_work_mem)
|
||||
free(pPVRTC_work_mem);
|
||||
return false;
|
||||
}
|
||||
|
||||
selector_sym = (int)m_selectors.size();
|
||||
selector_index = selector_history_buf[history_buf_index];
|
||||
|
||||
cur_selector_rle_count--;
|
||||
if (history_buf_index != 0)
|
||||
selector_history_buf.use(history_buf_index);
|
||||
}
|
||||
}
|
||||
|
||||
if (selector_sym >= (int)m_selectors.size())
|
||||
{
|
||||
assert(m_selector_history_buf_size > 0);
|
||||
|
||||
int history_buf_index = selector_sym - (int)m_selectors.size();
|
||||
|
||||
if (history_buf_index >= (int)selector_history_buf.size())
|
||||
else
|
||||
{
|
||||
// The file is corrupted or we've got a bug.
|
||||
BASISU_DEVEL_ERROR("basisu_lowlevel_transcoder::transcode_slice: invalid datastream (4)\n");
|
||||
if (pPVRTC_work_mem)
|
||||
free(pPVRTC_work_mem);
|
||||
return false;
|
||||
selector_index = selector_sym;
|
||||
|
||||
if (m_selector_history_buf_size)
|
||||
selector_history_buf.add(selector_index);
|
||||
}
|
||||
|
||||
selector_index = selector_history_buf[history_buf_index];
|
||||
|
||||
if (history_buf_index != 0)
|
||||
selector_history_buf.use(history_buf_index);
|
||||
}
|
||||
else
|
||||
{
|
||||
selector_index = selector_sym;
|
||||
|
||||
if (m_selector_history_buf_size)
|
||||
selector_history_buf.add(selector_index);
|
||||
}
|
||||
|
||||
prev_selector_index = selector_index;
|
||||
|
||||
if ((endpoint_index >= m_endpoints.size()) || (selector_index >= m_selectors.size()))
|
||||
{
|
||||
@@ -3893,6 +3924,19 @@ namespace basist
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_video)
|
||||
(*pPrevFrameIndices)[block_x + block_y * num_blocks_x] = endpoint_index | (selector_index << 16);
|
||||
|
||||
#if 0
|
||||
// Visualize CR's
|
||||
if ((is_video) && (pred == 2))
|
||||
{
|
||||
decoder_etc_block* pDst_block = reinterpret_cast<decoder_etc_block*>(static_cast<uint8_t*>(pDst_blocks) + (block_x + block_y * num_blocks_x) * output_stride);
|
||||
memset(pDst_block, 0xFF, 8);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
const endpoint *pEndpoint0 = &m_endpoints[endpoint_index];
|
||||
|
||||
block.set_base5_color(decoder_etc_block::pack_color5(pEndpoint0->m_color5, false));
|
||||
@@ -4005,7 +4049,7 @@ namespace basist
|
||||
|
||||
} // block_x
|
||||
|
||||
} // block-y
|
||||
} // block_y
|
||||
|
||||
if (endpoint_pred_repeat_count != 0)
|
||||
{
|
||||
@@ -4604,7 +4648,7 @@ namespace basist
|
||||
|
||||
return m_lowlevel_decoder.transcode_slice(pOutput_blocks, slice_desc.m_num_blocks_x, slice_desc.m_num_blocks_y,
|
||||
pDataU8 + slice_desc.m_file_ofs, slice_desc.m_file_size,
|
||||
fmt, output_stride, (decode_flags & cDecodeFlagsPVRTCWrapAddressing) != 0, (decode_flags & cDecodeFlagsBC1ForbidThreeColorBlocks) == 0);
|
||||
fmt, output_stride, (decode_flags & cDecodeFlagsPVRTCWrapAddressing) != 0, (decode_flags & cDecodeFlagsBC1ForbidThreeColorBlocks) == 0, *pHeader, slice_desc);
|
||||
}
|
||||
|
||||
int basisu_transcoder::find_first_slice_index(const void *pData, uint32_t data_size, uint32_t image_index, uint32_t level_index) const
|
||||
|
||||
@@ -73,7 +73,8 @@ namespace basist
|
||||
|
||||
bool decode_tables(const uint8_t *pTable_data, uint32_t table_data_size);
|
||||
|
||||
bool transcode_slice(void *pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t *pImage_data, uint32_t image_data_size, block_format fmt, uint32_t output_stride, bool wrap_addressing, bool bc1_allow_threecolor_blocks);
|
||||
bool transcode_slice(void *pDst_blocks, uint32_t num_blocks_x, uint32_t num_blocks_y, const uint8_t *pImage_data, uint32_t image_data_size, block_format fmt, uint32_t output_stride,
|
||||
bool wrap_addressing, bool bc1_allow_threecolor_blocks, const basis_file_header &header, const basis_slice_desc& slice_desc);
|
||||
|
||||
private:
|
||||
struct endpoint
|
||||
@@ -101,6 +102,10 @@ namespace basist
|
||||
};
|
||||
|
||||
std::vector<block_preds> m_block_endpoint_preds[2];
|
||||
|
||||
// Only used when decoding videos, for conditional replenishment.
|
||||
enum { cMaxPrevFrameLevels = 16 };
|
||||
std::vector<uint32_t> m_prev_frame_indices[2][cMaxPrevFrameLevels]; // [alpha_flag][level_index]
|
||||
};
|
||||
|
||||
struct basisu_slice_info
|
||||
|
||||
@@ -45,6 +45,10 @@ namespace basist
|
||||
const uint32_t ENDPOINT_PRED_MIN_REPEAT_COUNT = 3;
|
||||
const uint32_t ENDPOINT_PRED_COUNT_VLC_BITS = 4;
|
||||
|
||||
const uint32_t NUM_ENDPOINT_PREDS = 3;// BASISU_ARRAY_SIZE(g_endpoint_preds);
|
||||
const uint32_t CR_ENDPOINT_PRED_INDEX = NUM_ENDPOINT_PREDS - 1;
|
||||
const uint32_t NO_ENDPOINT_PRED_INDEX = 3;//NUM_ENDPOINT_PREDS;
|
||||
|
||||
const uint32_t MAX_SELECTOR_HISTORY_BUF_SIZE = 64;
|
||||
const uint32_t SELECTOR_HISTORY_BUF_RLE_COUNT_THRESH = 3;
|
||||
const uint32_t SELECTOR_HISTORY_BUF_RLE_COUNT_BITS = 6;
|
||||
|
||||
Reference in New Issue
Block a user