2019-09-27 02:16:44 +00:00
// basisu_backend.cpp
// Copyright (C) 2019 Binomial LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// TODO: This code originally supported full ETC1 and ETC1S, so there's some legacy stuff in here.
//
# include "basisu_backend.h"
# define BASISU_FASTER_SELECTOR_REORDERING 0
# define BASISU_BACKEND_VERIFY(c) verify(c, __LINE__);
namespace basisu
{
// TODO
static inline void verify ( bool condition , int line )
{
if ( ! condition )
{
fprintf ( stderr , " ERROR: basisu_backend: verify() failed at line %i! \n " , line ) ;
abort ( ) ;
}
}
basisu_backend : : basisu_backend ( )
{
clear ( ) ;
}
void basisu_backend : : clear ( )
{
m_pFront_end = NULL ;
m_params . clear ( ) ;
m_output . clear ( ) ;
}
void basisu_backend : : init ( basisu_frontend * pFront_end , basisu_backend_params & params , const basisu_backend_slice_desc_vec & slice_descs , const basist : : etc1_global_selector_codebook * pGlobal_sel_codebook )
{
m_pFront_end = pFront_end ;
m_params = params ;
m_slices = slice_descs ;
m_pGlobal_sel_codebook = pGlobal_sel_codebook ;
debug_printf ( " basisu_backend::Init: Slices: %u, ETC1S: %u, EndpointRDOQualityThresh: %f, SelectorRDOQualityThresh: %f, UseGlobalSelCodebook: %u, GlobalSelCodebookPalBits: %u, GlobalSelCodebookModBits: %u, Use hybrid selector codebooks: %u \n " ,
m_slices . size ( ) ,
params . m_etc1s ,
params . m_endpoint_rdo_quality_thresh ,
params . m_selector_rdo_quality_thresh ,
params . m_use_global_sel_codebook ,
params . m_global_sel_codebook_pal_bits ,
params . m_global_sel_codebook_mod_bits ,
params . m_use_hybrid_sel_codebooks ) ;
debug_printf ( " Frontend endpoints: %u selectors: %u \n " , m_pFront_end - > get_total_endpoint_clusters ( ) , m_pFront_end - > get_total_selector_clusters ( ) ) ;
for ( uint32_t i = 0 ; i < m_slices . size ( ) ; i + + )
{
debug_printf ( " Slice: %u, OrigWidth: %u, OrigHeight: %u, Width: %u, Height: %u, NumBlocksX: %u, NumBlocksY: %u, FirstBlockIndex: %u \n " ,
i ,
m_slices [ i ] . m_orig_width , m_slices [ i ] . m_orig_height ,
m_slices [ i ] . m_width , m_slices [ i ] . m_height ,
m_slices [ i ] . m_num_blocks_x , m_slices [ i ] . m_num_blocks_y ,
m_slices [ i ] . m_first_block_index ) ;
}
}
void basisu_backend : : create_endpoint_palette ( )
{
const basisu_frontend & r = * m_pFront_end ;
m_output . m_num_endpoints = r . get_total_endpoint_clusters ( ) ;
m_endpoint_palette . resize ( r . get_total_endpoint_clusters ( ) ) ;
for ( uint32_t i = 0 ; i < r . get_total_endpoint_clusters ( ) ; i + + )
{
etc1_endpoint_palette_entry & e = m_endpoint_palette [ i ] ;
e . m_color5_valid = r . get_endpoint_cluster_color_is_used ( i , false ) ;
e . m_color5 = r . get_endpoint_cluster_unscaled_color ( i , false ) ;
e . m_inten5 = r . get_endpoint_cluster_inten_table ( i , false ) ;
BASISU_BACKEND_VERIFY ( e . m_color5_valid ) ;
}
}
void basisu_backend : : create_selector_palette ( )
{
const basisu_frontend & r = * m_pFront_end ;
m_output . m_num_selectors = r . get_total_selector_clusters ( ) ;
m_selector_palette . resize ( r . get_total_selector_clusters ( ) ) ;
if ( m_params . m_use_global_sel_codebook )
{
m_global_selector_palette_desc . resize ( r . get_total_selector_clusters ( ) ) ;
for ( int i = 0 ; i < static_cast < int > ( r . get_total_selector_clusters ( ) ) ; i + + )
{
basist : : etc1_selector_palette_entry & selector_pal_entry = m_selector_palette [ i ] ;
etc1_global_selector_cb_entry_desc & pal_entry_desc = m_global_selector_palette_desc [ i ] ;
pal_entry_desc . m_pal_index = r . get_selector_cluster_global_selector_entry_ids ( ) [ i ] . m_palette_index ;
pal_entry_desc . m_mod_index = r . get_selector_cluster_global_selector_entry_ids ( ) [ i ] . m_modifier . get_index ( ) ;
pal_entry_desc . m_was_used = true ;
if ( m_params . m_use_hybrid_sel_codebooks )
pal_entry_desc . m_was_used = r . get_selector_cluster_uses_global_cb_vec ( ) [ i ] ;
if ( pal_entry_desc . m_was_used )
{
const etc_block & selector_bits = r . get_selector_cluster_selector_bits ( i ) ;
( void ) selector_bits ;
basist : : etc1_selector_palette_entry global_pal_entry ( m_pGlobal_sel_codebook - > get_entry ( r . get_selector_cluster_global_selector_entry_ids ( ) [ i ] ) ) ;
for ( uint32_t y = 0 ; y < 4 ; y + + )
{
for ( uint32_t x = 0 ; x < 4 ; x + + )
{
selector_pal_entry ( x , y ) = global_pal_entry ( x , y ) ;
assert ( selector_bits . get_selector ( x , y ) = = global_pal_entry ( x , y ) ) ;
}
}
}
else
{
const etc_block & selector_bits = r . get_selector_cluster_selector_bits ( i ) ;
for ( uint32_t y = 0 ; y < 4 ; y + + )
for ( uint32_t x = 0 ; x < 4 ; x + + )
selector_pal_entry [ y * 4 + x ] = static_cast < uint8_t > ( selector_bits . get_selector ( x , y ) ) ;
}
}
}
else
{
for ( uint32_t i = 0 ; i < r . get_total_selector_clusters ( ) ; i + + )
{
basist : : etc1_selector_palette_entry & s = m_selector_palette [ i ] ;
const etc_block & selector_bits = r . get_selector_cluster_selector_bits ( i ) ;
for ( uint32_t y = 0 ; y < 4 ; y + + )
{
for ( uint32_t x = 0 ; x < 4 ; x + + )
{
s [ y * 4 + x ] = static_cast < uint8_t > ( selector_bits . get_selector ( x , y ) ) ;
}
}
}
}
}
static const struct
{
int8_t m_dx , m_dy ;
} g_endpoint_preds [ ] =
{
{ - 1 , 0 } ,
{ 0 , - 1 } ,
{ - 1 , - 1 }
} ;
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 ) & & ( m_params . m_compression_level > 0 ) )
{
// We're changed the block endpoint indices, so we need to go and adjust the endpoint codebook (remove unused entries, optimize existing entries that have changed)
uint_vec new_block_endpoints ( get_total_blocks ( ) ) ;
for ( uint32_t slice_index = 0 ; slice_index < m_slices . size ( ) ; slice_index + + )
{
const uint32_t first_block_index = m_slices [ slice_index ] . m_first_block_index ;
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 ;
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 + + )
new_block_endpoints [ first_block_index + block_x + block_y * num_blocks_x ] = m_slice_encoder_blocks [ slice_index ] ( block_x , block_y ) . m_endpoint_index ;
}
int_vec old_to_new_endpoint_indices ;
r . reoptimize_remapped_endpoints ( new_block_endpoints , old_to_new_endpoint_indices , true ) ;
create_endpoint_palette ( ) ;
for ( uint32_t slice_index = 0 ; slice_index < m_slices . size ( ) ; slice_index + + )
{
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 ;
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 + + )
{
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 ) ;
m . m_endpoint_index = old_to_new_endpoint_indices [ m . m_endpoint_index ] ;
} // block_x
} // block_y
} // slice_index
for ( uint32_t i = 0 ; i < all_endpoint_indices . size ( ) ; i + + )
all_endpoint_indices [ i ] = old_to_new_endpoint_indices [ all_endpoint_indices [ i ] ] ;
} //if (total_block_endpoints_remapped)
// Sort endpoint codebook
palette_index_reorderer reorderer ;
reorderer . init ( ( uint32_t ) all_endpoint_indices . size ( ) , & all_endpoint_indices [ 0 ] , r . get_total_endpoint_clusters ( ) , nullptr , nullptr , 0 ) ;
m_endpoint_remap_table_old_to_new = reorderer . get_remap_table ( ) ;
m_endpoint_remap_table_new_to_old . resize ( r . get_total_endpoint_clusters ( ) ) ;
for ( uint32_t i = 0 ; i < m_endpoint_remap_table_old_to_new . size ( ) ; i + + )
m_endpoint_remap_table_new_to_old [ m_endpoint_remap_table_old_to_new [ i ] ] = i ;
}
void basisu_backend : : sort_selector_codebook ( )
{
basisu_frontend & r = * m_pFront_end ;
m_selector_remap_table_new_to_old . resize ( r . get_total_selector_clusters ( ) ) ;
if ( m_params . m_compression_level = = 0 )
{
for ( uint32_t i = 0 ; i < r . get_total_selector_clusters ( ) ; i + + )
m_selector_remap_table_new_to_old [ i ] = i ;
}
else
{
m_selector_remap_table_new_to_old [ 0 ] = 0 ;
uint32_t prev_selector_index = 0 ;
int_vec remaining_selectors ;
remaining_selectors . reserve ( r . get_total_selector_clusters ( ) - 1 ) ;
for ( uint32_t i = 1 ; i < r . get_total_selector_clusters ( ) ; i + + )
remaining_selectors . push_back ( i ) ;
uint_vec selector_palette_bytes ( m_selector_palette . size ( ) ) ;
for ( uint32_t i = 0 ; i < m_selector_palette . size ( ) ; i + + )
selector_palette_bytes [ i ] = m_selector_palette [ i ] . get_byte ( 0 ) | ( m_selector_palette [ i ] . get_byte ( 1 ) < < 8 ) | ( m_selector_palette [ i ] . get_byte ( 2 ) < < 16 ) | ( m_selector_palette [ i ] . get_byte ( 3 ) < < 24 ) ;
// This is the traveling salesman problem.
for ( uint32_t i = 1 ; i < r . get_total_selector_clusters ( ) ; i + + )
{
uint32_t best_hamming_dist = 100 ;
uint32_t best_index = 0 ;
# if BASISU_FASTER_SELECTOR_REORDERING
const uint32_t step = ( remaining_selectors . size ( ) > 16 ) ? 16 : 1 ;
for ( uint32_t j = 0 ; j < remaining_selectors . size ( ) ; j + = step )
# else
for ( uint32_t j = 0 ; j < remaining_selectors . size ( ) ; j + + )
# endif
{
int selector_index = remaining_selectors [ j ] ;
uint32_t k = selector_palette_bytes [ prev_selector_index ] ^ selector_palette_bytes [ selector_index ] ;
uint32_t hamming_dist = g_hamming_dist [ k & 0xFF ] + g_hamming_dist [ ( k > > 8 ) & 0xFF ] + g_hamming_dist [ ( k > > 16 ) & 0xFF ] + g_hamming_dist [ k > > 24 ] ;
if ( hamming_dist < best_hamming_dist )
{
best_hamming_dist = hamming_dist ;
best_index = j ;
if ( best_hamming_dist < = 1 )
break ;
}
}
prev_selector_index = remaining_selectors [ best_index ] ;
m_selector_remap_table_new_to_old [ i ] = prev_selector_index ;
remaining_selectors [ best_index ] = remaining_selectors . back ( ) ;
remaining_selectors . resize ( remaining_selectors . size ( ) - 1 ) ;
}
}
m_selector_remap_table_old_to_new . resize ( r . get_total_selector_clusters ( ) ) ;
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 ! = ( ( int ) 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 ( ) ) ;
uint32_t total_endpoint_pred_missed = 0 , total_endpoint_pred_hits = 0 , total_block_endpoints_remapped = 0 ;
uint_vec all_endpoint_indices ;
all_endpoint_indices . reserve ( get_total_blocks ( ) ) ;
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 ;
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 ;
m_slice_encoder_blocks [ slice_index ] . resize ( num_blocks_x , num_blocks_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 + + )
{
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 ) ;
m . m_endpoint_index = r . get_subblock_endpoint_cluster_index ( block_index , 0 ) ;
BASISU_BACKEND_VERIFY ( r . get_subblock_endpoint_cluster_index ( block_index , 0 ) = = r . get_subblock_endpoint_cluster_index ( block_index , 1 ) ) ;
m . m_selector_index = r . get_block_selector_cluster_index ( block_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 < basist : : NUM_ENDPOINT_PREDS ; endpoint_pred + + )
{
if ( ( is_video ) & & ( endpoint_pred = = basist : : CR_ENDPOINT_PRED_INDEX ) )
{
if ( ( prev_frame_slice_index ! = - 1 ) & & ( ! is_iframe ) )
{
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 ;
}
}
}
} // endpoint_pred
if ( best_endpoint_pred ! = UINT32_MAX )
{
m . m_endpoint_predictor = best_endpoint_pred ;
total_endpoint_pred_hits + + ;
}
else if ( m_params . m_endpoint_rdo_quality_thresh > 0.0f )
{
const pixel_block & src_pixels = r . get_source_pixel_block ( block_index ) ;
etc_block etc_blk ( r . get_output_block ( block_index ) ) ;
uint64_t cur_err = etc_blk . evaluate_etc1_error ( src_pixels . get_ptr ( ) , r . get_params ( ) . m_perceptual ) ;
if ( cur_err )
{
const uint64_t thresh_err = ( uint64_t ) ( cur_err * maximum ( 1.0f , m_params . m_endpoint_rdo_quality_thresh ) ) ;
etc_block trial_etc_block ( etc_blk ) ;
uint64_t best_err = UINT64_MAX ;
uint32_t best_endpoint_index = 0 ;
best_endpoint_pred = UINT32_MAX ;
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 ;
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_index = m_slice_encoder_blocks [ slice_index ] ( pred_block_x , pred_block_y ) . m_endpoint_index ;
uint32_t pred_inten = r . get_endpoint_cluster_inten_table ( pred_endpoint_index , false ) ;
color_rgba pred_color = r . get_endpoint_cluster_unscaled_color ( pred_endpoint_index , false ) ;
trial_etc_block . set_block_color5 ( pred_color , pred_color ) ;
trial_etc_block . set_inten_table ( 0 , pred_inten ) ;
trial_etc_block . set_inten_table ( 1 , pred_inten ) ;
color_rgba trial_colors [ 16 ] ;
unpack_etc1 ( trial_etc_block , trial_colors ) ;
uint64_t trial_err = 0 ;
for ( uint32_t p = 0 ; p < 16 ; p + + )
{
trial_err + = color_distance ( r . get_params ( ) . m_perceptual , src_pixels . get_ptr ( ) [ p ] , trial_colors [ p ] , false ) ;
if ( trial_err > thresh_err )
break ;
}
if ( trial_err < = thresh_err )
{
if ( ( trial_err < best_err ) | | ( ( trial_err = = best_err ) & & ( endpoint_pred < best_endpoint_pred ) ) )
{
best_endpoint_pred = endpoint_pred ;
best_err = trial_err ;
best_endpoint_index = pred_endpoint_index ;
}
}
} // endpoint_pred
if ( best_endpoint_pred ! = UINT32_MAX )
{
m . m_endpoint_index = best_endpoint_index ;
m . m_endpoint_predictor = best_endpoint_pred ;
total_endpoint_pred_hits + + ;
total_block_endpoints_remapped + + ;
}
else
{
total_endpoint_pred_missed + + ;
}
}
}
else
{
total_endpoint_pred_missed + + ;
}
if ( m . m_endpoint_predictor = = basist : : NO_ENDPOINT_PRED_INDEX )
{
all_endpoint_indices . push_back ( m . m_endpoint_index ) ;
}
} // block_x
} // block_y
} // slice
debug_printf ( " total_endpoint_pred_missed: %u (%3.2f%%) total_endpoint_pred_hit: %u (%3.2f%%), total_block_endpoints_remapped: %u (%3.2f%%) \n " ,
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 ( )
{
for ( uint32_t slice_index = 0 ; slice_index < m_slices . size ( ) ; slice_index + + )
{
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 ;
gpu_image gi ;
2020-02-13 14:56:46 +00:00
gi . init ( texture_format : : cETC1 , width , height ) ;
2019-09-27 02:16:44 +00:00
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 + + )
{
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 ) ;
{
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 ] ;
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 ) ) ;
}
} // block_x
} // block_y
m_output . m_slice_image_crcs [ slice_index ] = basist : : crc16 ( gi . get_ptr ( ) , gi . get_size_in_bytes ( ) , 0 ) ;
if ( m_params . m_debug_images )
{
image gi_unpacked ;
gi . unpack ( gi_unpacked ) ;
char buf [ 256 ] ;
# ifdef _WIN32
sprintf_s ( buf , sizeof ( buf ) , " basisu_backend_slice_%u.png " , slice_index ) ;
# else
snprintf ( buf , sizeof ( buf ) , " basisu_backend_slice_%u.png " , slice_index ) ;
# endif
save_png ( buf , gi_unpacked ) ;
}
} // slice_index
}
// TODO: Split this into multiple methods.
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 ;
basist : : approx_move_to_front selector_history_buf ( basist : : MAX_SELECTOR_HISTORY_BUF_SIZE ) ;
histogram selector_history_buf_histogram ( basist : : MAX_SELECTOR_HISTORY_BUF_SIZE ) ;
histogram selector_histogram ( r . get_total_selector_clusters ( ) + basist : : MAX_SELECTOR_HISTORY_BUF_SIZE + 1 ) ;
histogram selector_history_buf_rle_histogram ( 1 < < basist : : SELECTOR_HISTORY_BUF_RLE_COUNT_BITS ) ;
std : : vector < uint_vec > selector_syms ( m_slices . size ( ) ) ;
const uint32_t SELECTOR_HISTORY_BUF_FIRST_SYMBOL_INDEX = r . get_total_selector_clusters ( ) ;
const uint32_t SELECTOR_HISTORY_BUF_RLE_SYMBOL_INDEX = SELECTOR_HISTORY_BUF_FIRST_SYMBOL_INDEX + basist : : MAX_SELECTOR_HISTORY_BUF_SIZE ;
m_output . m_slice_image_crcs . resize ( m_slices . size ( ) ) ;
histogram delta_endpoint_histogram ( r . get_total_endpoint_clusters ( ) ) ;
histogram endpoint_pred_histogram ( basist : : ENDPOINT_PRED_TOTAL_SYMBOLS ) ;
std : : vector < uint_vec > endpoint_pred_syms ( m_slices . size ( ) ) ;
uint32_t total_endpoint_indices_remapped = 0 ;
uint_vec block_endpoint_indices , block_selector_indices ;
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 ;
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 ;
selector_history_buf . reset ( ) ;
int selector_history_buf_rle_count = 0 ;
int prev_endpoint_pred_sym_bits = - 1 , endpoint_pred_repeat_count = 0 ;
uint32_t prev_endpoint_index = 0 ;
vector2D < uint8_t > block_endpoints_are_referenced ( num_blocks_x , num_blocks_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 + + )
{
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 ) ;
if ( m . m_endpoint_predictor = = 0 )
block_endpoints_are_referenced ( block_x - 1 , block_y ) = true ;
else if ( m . m_endpoint_predictor = = 1 )
block_endpoints_are_referenced ( block_x , block_y - 1 ) = true ;
else if ( m . m_endpoint_predictor = = 2 )
{
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 + + )
{
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 ) ;
if ( ( ( block_x & 1 ) = = 0 ) & & ( ( block_y & 1 ) = = 0 ) )
{
uint32_t endpoint_pred_cur_sym_bits = 0 ;
for ( uint32_t y = 0 ; y < 2 ; y + + )
{
for ( uint32_t x = 0 ; x < 2 ; x + + )
{
const uint32_t bx = block_x + x ;
const uint32_t by = block_y + y ;
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 ;
endpoint_pred_cur_sym_bits | = ( pred < < ( x * 2 + y * 4 ) ) ;
}
}
if ( ( int ) endpoint_pred_cur_sym_bits = = prev_endpoint_pred_sym_bits )
{
endpoint_pred_repeat_count + + ;
}
else
{
if ( endpoint_pred_repeat_count > 0 )
{
if ( endpoint_pred_repeat_count > ( int ) basist : : ENDPOINT_PRED_MIN_REPEAT_COUNT )
{
endpoint_pred_histogram . inc ( basist : : ENDPOINT_PRED_REPEAT_LAST_SYMBOL ) ;
endpoint_pred_syms [ slice_index ] . push_back ( basist : : ENDPOINT_PRED_REPEAT_LAST_SYMBOL ) ;
endpoint_pred_syms [ slice_index ] . push_back ( endpoint_pred_repeat_count ) ;
}
else
{
for ( int j = 0 ; j < endpoint_pred_repeat_count ; j + + )
{
endpoint_pred_histogram . inc ( prev_endpoint_pred_sym_bits ) ;
endpoint_pred_syms [ slice_index ] . push_back ( prev_endpoint_pred_sym_bits ) ;
}
}
endpoint_pred_repeat_count = 0 ;
}
endpoint_pred_histogram . inc ( endpoint_pred_cur_sym_bits ) ;
endpoint_pred_syms [ slice_index ] . push_back ( endpoint_pred_cur_sym_bits ) ;
prev_endpoint_pred_sym_bits = endpoint_pred_cur_sym_bits ;
}
}
int new_endpoint_index = m_endpoint_remap_table_old_to_new [ m . m_endpoint_index ] ;
if ( m . m_endpoint_predictor = = basist : : NO_ENDPOINT_PRED_INDEX )
{
int endpoint_delta = new_endpoint_index - prev_endpoint_index ;
if ( ( m_params . m_endpoint_rdo_quality_thresh > 1.0f ) & & ( iabs ( endpoint_delta ) > 1 ) & & ( ! block_endpoints_are_referenced ( block_x , block_y ) ) )
{
const pixel_block & src_pixels = r . get_source_pixel_block ( block_index ) ;
etc_block etc_blk ( r . get_output_block ( block_index ) ) ;
const uint64_t cur_err = etc_blk . evaluate_etc1_error ( src_pixels . get_ptr ( ) , r . get_params ( ) . m_perceptual ) ;
if ( cur_err )
{
const float endpoint_remap_thresh = maximum ( 1.0f , m_params . m_endpoint_rdo_quality_thresh ) ;
const uint64_t thresh_err = ( uint64_t ) ( cur_err * endpoint_remap_thresh ) ;
uint64_t best_trial_err = UINT64_MAX ;
int best_trial_idx = 0 ;
etc_block trial_etc_blk ( etc_blk ) ;
const int MAX_ENDPOINT_SEARCH_DIST = 32 ;
const int search_dist = minimum < int > ( iabs ( endpoint_delta ) - 1 , MAX_ENDPOINT_SEARCH_DIST ) ;
for ( int d = - search_dist ; d < search_dist ; d + + )
{
int trial_idx = prev_endpoint_index + d ;
if ( trial_idx < 0 )
trial_idx + = ( int ) r . get_total_endpoint_clusters ( ) ;
else if ( trial_idx > = ( int ) r . get_total_endpoint_clusters ( ) )
trial_idx - = ( int ) r . get_total_endpoint_clusters ( ) ;
if ( trial_idx = = new_endpoint_index )
continue ;
const etc1_endpoint_palette_entry & p = m_endpoint_palette [ m_endpoint_remap_table_new_to_old [ trial_idx ] ] ;
trial_etc_blk . set_block_color5_etc1s ( p . m_color5 ) ;
trial_etc_blk . set_inten_tables_etc1s ( p . m_inten5 ) ;
uint64_t trial_err = trial_etc_blk . evaluate_etc1_error ( src_pixels . get_ptr ( ) , r . get_params ( ) . m_perceptual ) ;
if ( trial_err < = thresh_err )
{
if ( trial_err < best_trial_err )
{
best_trial_err = trial_err ;
best_trial_idx = trial_idx ;
}
}
}
if ( best_trial_err ! = UINT64_MAX )
{
m . m_endpoint_index = m_endpoint_remap_table_new_to_old [ best_trial_idx ] ;
new_endpoint_index = best_trial_idx ;
endpoint_delta = new_endpoint_index - prev_endpoint_index ;
total_endpoint_indices_remapped + + ;
}
}
}
if ( endpoint_delta < 0 )
endpoint_delta + = ( int ) r . get_total_endpoint_clusters ( ) ;
delta_endpoint_histogram . inc ( endpoint_delta ) ;
}
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 ) ;
etc_block etc_blk ( r . get_output_block ( block_index ) ) ;
color_rgba etc_blk_unpacked [ 16 ] ;
unpack_etc1 ( etc_blk , etc_blk_unpacked ) ;
uint64_t cur_err = 0 ;
for ( uint32_t p = 0 ; p < 16 ; p + + )
cur_err + = color_distance ( r . get_params ( ) . m_perceptual , src_pixels . get_ptr ( ) [ p ] , etc_blk_unpacked [ p ] , false ) ;
uint64_t best_trial_err = UINT64_MAX ;
int best_trial_idx = 0 ;
uint32_t best_trial_history_buf_idx = 0 ;
const float selector_remap_thresh = maximum ( 1.0f , m_params . m_selector_rdo_quality_thresh ) ; //2.5f;
const bool use_strict_search = ( m_params . m_compression_level = = 0 ) & & ( selector_remap_thresh = = 1.0f ) ;
for ( uint32_t j = 0 ; j < selector_history_buf . size ( ) ; j + + )
{
const int trial_idx = selector_history_buf [ j ] ;
if ( use_strict_search )
{
if ( trial_idx = = new_selector_index )
{
best_trial_err = 0 ;
best_trial_idx = trial_idx ;
best_trial_history_buf_idx = j ;
break ;
}
}
else
{
for ( uint32_t sy = 0 ; sy < 4 ; sy + + )
for ( uint32_t sx = 0 ; sx < 4 ; sx + + )
etc_blk . set_selector ( sx , sy , m_selector_palette [ m_selector_remap_table_new_to_old [ trial_idx ] ] ( sx , sy ) ) ;
// TODO: Optimize this
unpack_etc1 ( etc_blk , etc_blk_unpacked ) ;
uint64_t trial_err = 0 ;
const uint64_t thresh_err = minimum ( ( uint64_t ) ceilf ( cur_err * selector_remap_thresh ) , best_trial_err ) ;
for ( uint32_t p = 0 ; p < 16 ; p + + )
{
trial_err + = color_distance ( r . get_params ( ) . m_perceptual , src_pixels . get_ptr ( ) [ p ] , etc_blk_unpacked [ p ] , false ) ;
if ( trial_err > thresh_err )
break ;
}
if ( trial_err < = cur_err * selector_remap_thresh )
{
if ( trial_err < best_trial_err )
{
best_trial_err = trial_err ;
best_trial_idx = trial_idx ;
best_trial_history_buf_idx = j ;
}
}
}
}
if ( best_trial_err ! = UINT64_MAX )
{
if ( new_selector_index ! = best_trial_idx )
total_selector_indices_remapped + + ;
new_selector_index = best_trial_idx ;
total_used_selector_history_buf + + ;
selector_history_buf_index = best_trial_history_buf_idx ;
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 ] ;
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 )
{
selector_syms [ slice_index ] . push_back ( SELECTOR_HISTORY_BUF_RLE_SYMBOL_INDEX ) ;
selector_syms [ slice_index ] . push_back ( selector_history_buf_rle_count ) ;
int run_sym = selector_history_buf_rle_count - basist : : SELECTOR_HISTORY_BUF_RLE_COUNT_THRESH ;
if ( run_sym > = ( ( int ) basist : : SELECTOR_HISTORY_BUF_RLE_COUNT_TOTAL - 1 ) )
selector_history_buf_rle_histogram . inc ( basist : : SELECTOR_HISTORY_BUF_RLE_COUNT_TOTAL - 1 ) ;
else
selector_history_buf_rle_histogram . inc ( run_sym ) ;
selector_histogram . inc ( SELECTOR_HISTORY_BUF_RLE_SYMBOL_INDEX ) ;
}
else
{
for ( int k = 0 ; k < selector_history_buf_rle_count ; k + + )
{
uint32_t sym_index = SELECTOR_HISTORY_BUF_FIRST_SYMBOL_INDEX + 0 ;
selector_syms [ slice_index ] . push_back ( sym_index ) ;
selector_histogram . inc ( sym_index ) ;
}
}
selector_history_buf_rle_count = 0 ;
}
if ( selector_history_buf_index > = 0 )
{
if ( selector_history_buf_index = = 0 )
selector_history_buf_rle_count + + ;
else
{
uint32_t history_buf_sym = SELECTOR_HISTORY_BUF_FIRST_SYMBOL_INDEX + selector_history_buf_index ;
selector_syms [ slice_index ] . push_back ( history_buf_sym ) ;
selector_histogram . inc ( history_buf_sym ) ;
}
}
else
{
selector_syms [ slice_index ] . push_back ( new_selector_index ) ;
selector_histogram . inc ( new_selector_index ) ;
}
m . m_selector_history_buf_index = selector_history_buf_index ;
if ( selector_history_buf_index < 0 )
selector_history_buf . add ( new_selector_index ) ;
else if ( selector_history_buf . size ( ) )
selector_history_buf . use ( selector_history_buf_index ) ;
}
block_selector_indices . push_back ( m . m_selector_index ) ;
} // block_x
} // block_y
if ( endpoint_pred_repeat_count > 0 )
{
if ( endpoint_pred_repeat_count > ( int ) basist : : ENDPOINT_PRED_MIN_REPEAT_COUNT )
{
endpoint_pred_histogram . inc ( basist : : ENDPOINT_PRED_REPEAT_LAST_SYMBOL ) ;
endpoint_pred_syms [ slice_index ] . push_back ( basist : : ENDPOINT_PRED_REPEAT_LAST_SYMBOL ) ;
endpoint_pred_syms [ slice_index ] . push_back ( endpoint_pred_repeat_count ) ;
}
else
{
for ( int j = 0 ; j < endpoint_pred_repeat_count ; j + + )
{
endpoint_pred_histogram . inc ( prev_endpoint_pred_sym_bits ) ;
endpoint_pred_syms [ slice_index ] . push_back ( prev_endpoint_pred_sym_bits ) ;
}
}
endpoint_pred_repeat_count = 0 ;
}
if ( selector_history_buf_rle_count )
{
if ( selector_history_buf_rle_count > = ( int ) basist : : SELECTOR_HISTORY_BUF_RLE_COUNT_THRESH )
{
selector_syms [ slice_index ] . push_back ( SELECTOR_HISTORY_BUF_RLE_SYMBOL_INDEX ) ;
selector_syms [ slice_index ] . push_back ( selector_history_buf_rle_count ) ;
int run_sym = selector_history_buf_rle_count - basist : : SELECTOR_HISTORY_BUF_RLE_COUNT_THRESH ;
if ( run_sym > = ( ( int ) basist : : SELECTOR_HISTORY_BUF_RLE_COUNT_TOTAL - 1 ) )
selector_history_buf_rle_histogram . inc ( basist : : SELECTOR_HISTORY_BUF_RLE_COUNT_TOTAL - 1 ) ;
else
selector_history_buf_rle_histogram . inc ( run_sym ) ;
selector_histogram . inc ( SELECTOR_HISTORY_BUF_RLE_SYMBOL_INDEX ) ;
}
else
{
for ( int i = 0 ; i < selector_history_buf_rle_count ; i + + )
{
uint32_t sym_index = SELECTOR_HISTORY_BUF_FIRST_SYMBOL_INDEX + 0 ;
selector_syms [ slice_index ] . push_back ( sym_index ) ;
selector_histogram . inc ( sym_index ) ;
}
}
selector_history_buf_rle_count = 0 ;
}
} // slice_index
debug_printf ( " Endpoint pred RDO total endpoint indices remapped: %u %3.2f%% \n " ,
total_endpoint_indices_remapped , total_endpoint_indices_remapped * 100.0f / get_total_blocks ( ) ) ;
debug_printf ( " Selector history RDO total selector indices remapped: %u %3.2f%%, Used history buf: %u %3.2f%% \n " ,
total_selector_indices_remapped , total_selector_indices_remapped * 100.0f / get_total_blocks ( ) ,
total_used_selector_history_buf , total_used_selector_history_buf * 100.0f / get_total_blocks ( ) ) ;
if ( ( total_endpoint_indices_remapped ) & & ( m_params . m_compression_level > 0 ) )
{
int_vec unused ;
r . reoptimize_remapped_endpoints ( block_endpoint_indices , unused , false , & block_selector_indices ) ;
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 ( ) ;
double delta_endpoint_entropy = delta_endpoint_histogram . get_entropy ( ) / delta_endpoint_histogram . get_total ( ) ;
double selector_entropy = selector_histogram . get_entropy ( ) / selector_histogram . get_total ( ) ;
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 ) )
{
error_printf ( " endpoint_pred_model.init() failed! " ) ;
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 ) )
{
error_printf ( " delta_endpoint_model.init() failed! " ) ;
return false ;
}
if ( ! selector_histogram . get_total ( ) )
selector_histogram . inc ( 0 ) ;
huffman_encoding_table selector_model ;
if ( ! selector_model . init ( selector_histogram , 16 ) )
{
error_printf ( " selector_model.init() failed! " ) ;
return false ;
}
if ( ! selector_history_buf_rle_histogram . get_total ( ) )
selector_history_buf_rle_histogram . inc ( 0 ) ;
huffman_encoding_table selector_history_buf_rle_model ;
if ( ! selector_history_buf_rle_model . init ( selector_history_buf_rle_histogram , 16 ) )
{
error_printf ( " selector_history_buf_rle_model.init() failed! " ) ;
return false ;
}
bitwise_coder coder ;
coder . init ( 1024 * 1024 * 4 ) ;
uint32_t endpoint_pred_model_bits = coder . emit_huffman_table ( endpoint_pred_model ) ;
uint32_t delta_endpoint_bits = coder . emit_huffman_table ( delta_endpoint_model ) ;
uint32_t selector_model_bits = coder . emit_huffman_table ( selector_model ) ;
uint32_t selector_history_buf_run_sym_bits = coder . emit_huffman_table ( selector_history_buf_rle_model ) ;
coder . put_bits ( basist : : MAX_SELECTOR_HISTORY_BUF_SIZE , 13 ) ;
debug_printf ( " Model sizes: EndpointPred: %u bits %u bytes (%3.3f bpp) DeltaEndpoint: %u bits %u bytes (%3.3f bpp) Selector: %u bits %u bytes (%3.3f bpp) SelectorHistBufRLE: %u bits %u bytes (%3.3f bpp) \n " ,
endpoint_pred_model_bits , ( endpoint_pred_model_bits + 7 ) / 8 , endpoint_pred_model_bits / float ( get_total_input_texels ( ) ) ,
delta_endpoint_bits , ( delta_endpoint_bits + 7 ) / 8 , delta_endpoint_bits / float ( get_total_input_texels ( ) ) ,
selector_model_bits , ( selector_model_bits + 7 ) / 8 , selector_model_bits / float ( get_total_input_texels ( ) ) ,
selector_history_buf_run_sym_bits , ( selector_history_buf_run_sym_bits + 7 ) / 8 , selector_history_buf_run_sym_bits / float ( get_total_input_texels ( ) ) ) ;
coder . flush ( ) ;
m_output . m_slice_image_tables = coder . get_bytes ( ) ;
uint32_t total_endpoint_pred_bits = 0 , total_delta_endpoint_bits = 0 , total_selector_bits = 0 ;
uint32_t total_image_bytes = 0 ;
m_output . m_slice_image_data . resize ( m_slices . size ( ) ) ;
for ( uint32_t slice_index = 0 ; slice_index < m_slices . size ( ) ; slice_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 ;
coder . init ( 1024 * 1024 * 4 ) ;
uint32_t cur_selector_sym_ofs = 0 ;
uint32_t selector_rle_count = 0 ;
int endpoint_pred_repeat_count = 0 ;
uint32_t cur_endpoint_pred_sym_ofs = 0 ;
// uint32_t prev_endpoint_pred_sym = 0;
uint32_t prev_endpoint_index = 0 ;
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 + + )
{
const encoder_block & m = m_slice_encoder_blocks [ slice_index ] ( block_x , block_y ) ;
if ( ( ( block_x & 1 ) = = 0 ) & & ( ( block_y & 1 ) = = 0 ) )
{
if ( endpoint_pred_repeat_count > 0 )
{
endpoint_pred_repeat_count - - ;
}
else
{
uint32_t sym = endpoint_pred_syms [ slice_index ] [ cur_endpoint_pred_sym_ofs + + ] ;
if ( sym = = basist : : ENDPOINT_PRED_REPEAT_LAST_SYMBOL )
{
total_endpoint_pred_bits + = coder . put_code ( sym , endpoint_pred_model ) ;
endpoint_pred_repeat_count = endpoint_pred_syms [ slice_index ] [ cur_endpoint_pred_sym_ofs + + ] ;
assert ( endpoint_pred_repeat_count > = ( int ) basist : : ENDPOINT_PRED_MIN_REPEAT_COUNT ) ;
total_endpoint_pred_bits + = coder . put_vlc ( endpoint_pred_repeat_count - basist : : ENDPOINT_PRED_MIN_REPEAT_COUNT , basist : : ENDPOINT_PRED_COUNT_VLC_BITS ) ;
endpoint_pred_repeat_count - - ;
}
else
{
total_endpoint_pred_bits + = coder . put_code ( sym , endpoint_pred_model ) ;
//prev_endpoint_pred_sym = sym;
}
}
}
const int new_endpoint_index = m_endpoint_remap_table_old_to_new [ m . m_endpoint_index ] ;
if ( m . m_endpoint_predictor = = basist : : NO_ENDPOINT_PRED_INDEX )
{
int endpoint_delta = new_endpoint_index - prev_endpoint_index ;
if ( endpoint_delta < 0 )
endpoint_delta + = ( int ) r . get_total_endpoint_clusters ( ) ;
total_delta_endpoint_bits + = coder . put_code ( endpoint_delta , delta_endpoint_model ) ;
}
prev_endpoint_index = new_endpoint_index ;
if ( ( ! is_video ) | | ( m . m_endpoint_predictor ! = basist : : CR_ENDPOINT_PRED_INDEX ) )
{
if ( ! selector_rle_count )
{
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 )
{
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
} // block_y
BASISU_BACKEND_VERIFY ( cur_endpoint_pred_sym_ofs = = endpoint_pred_syms [ slice_index ] . size ( ) ) ;
BASISU_BACKEND_VERIFY ( cur_selector_sym_ofs = = selector_syms [ slice_index ] . size ( ) ) ;
coder . flush ( ) ;
m_output . m_slice_image_data [ slice_index ] = coder . get_bytes ( ) ;
total_image_bytes + = ( uint32_t ) coder . get_bytes ( ) . size ( ) ;
debug_printf ( " Slice %u compressed size: %u bytes, %3.3f bits per slice texel \n " , slice_index , m_output . m_slice_image_data [ slice_index ] . size ( ) , m_output . m_slice_image_data [ slice_index ] . size ( ) * 8.0f / ( m_slices [ slice_index ] . m_orig_width * m_slices [ slice_index ] . m_orig_height ) ) ;
} // slice_index
const double total_texels = static_cast < double > ( get_total_input_texels ( ) ) ;
const double total_blocks = static_cast < double > ( get_total_blocks ( ) ) ;
debug_printf ( " Total endpoint pred bits: %u bytes: %u bits/texel: %3.3f bits/block: %3.3f \n " , total_endpoint_pred_bits , total_endpoint_pred_bits / 8 , total_endpoint_pred_bits / total_texels , total_endpoint_pred_bits / total_blocks ) ;
debug_printf ( " Total delta endpoint bits: %u bytes: %u bits/texel: %3.3f bits/block: %3.3f \n " , total_delta_endpoint_bits , total_delta_endpoint_bits / 8 , total_delta_endpoint_bits / total_texels , total_delta_endpoint_bits / total_blocks ) ;
debug_printf ( " Total selector bits: %u bytes: %u bits/texel: %3.3f bits/block: %3.3f \n " , total_selector_bits , total_selector_bits / 8 , total_selector_bits / total_texels , total_selector_bits / total_blocks ) ;
debug_printf ( " Total table bytes: %u, %3.3f bits/texel \n " , m_output . m_slice_image_tables . size ( ) , m_output . m_slice_image_tables . size ( ) * 8.0f / total_texels ) ;
debug_printf ( " Total image bytes: %u, %3.3f bits/texel \n " , total_image_bytes , total_image_bytes * 8.0f / total_texels ) ;
return true ;
}
bool basisu_backend : : encode_endpoint_palette ( )
{
const basisu_frontend & r = * m_pFront_end ;
// Maps NEW to OLD endpoints
uint_vec endpoint_remap_table_inv ( r . get_total_endpoint_clusters ( ) ) ;
for ( uint32_t old_endpoint_index = 0 ; old_endpoint_index < m_endpoint_remap_table_old_to_new . size ( ) ; old_endpoint_index + + )
endpoint_remap_table_inv [ m_endpoint_remap_table_old_to_new [ old_endpoint_index ] ] = old_endpoint_index ;
bool is_grayscale = true ;
for ( uint32_t old_endpoint_index = 0 ; old_endpoint_index < ( uint32_t ) m_endpoint_palette . size ( ) ; old_endpoint_index + + )
{
int r5 = m_endpoint_palette [ old_endpoint_index ] . m_color5 [ 0 ] ;
int g5 = m_endpoint_palette [ old_endpoint_index ] . m_color5 [ 1 ] ;
int b5 = m_endpoint_palette [ old_endpoint_index ] . m_color5 [ 2 ] ;
if ( ( r5 ! = g5 ) | | ( r5 ! = b5 ) )
{
is_grayscale = false ;
break ;
}
}
histogram color5_delta_hist0 ( 32 ) ; // prev 0-9, delta is -9 to 31
histogram color5_delta_hist1 ( 32 ) ; // prev 10-21, delta is -21 to 21
histogram color5_delta_hist2 ( 32 ) ; // prev 22-31, delta is -31 to 9
histogram inten_delta_hist ( 8 ) ;
color_rgba prev_color5 ( 16 , 16 , 16 , 0 ) ;
uint32_t prev_inten = 0 ;
for ( uint32_t new_endpoint_index = 0 ; new_endpoint_index < r . get_total_endpoint_clusters ( ) ; new_endpoint_index + + )
{
const uint32_t old_endpoint_index = endpoint_remap_table_inv [ new_endpoint_index ] ;
int delta_inten = m_endpoint_palette [ old_endpoint_index ] . m_inten5 - prev_inten ;
inten_delta_hist . inc ( delta_inten & 7 ) ;
prev_inten = m_endpoint_palette [ old_endpoint_index ] . m_inten5 ;
for ( uint32_t i = 0 ; i < ( is_grayscale ? 1U : 3U ) ; i + + )
{
const int delta = ( m_endpoint_palette [ old_endpoint_index ] . m_color5 [ i ] - prev_color5 [ i ] ) & 31 ;
if ( prev_color5 [ i ] < = basist : : COLOR5_PAL0_PREV_HI )
color5_delta_hist0 . inc ( delta ) ;
else if ( prev_color5 [ i ] < = basist : : COLOR5_PAL1_PREV_HI )
color5_delta_hist1 . inc ( delta ) ;
else
color5_delta_hist2 . inc ( delta ) ;
prev_color5 [ i ] = m_endpoint_palette [ old_endpoint_index ] . m_color5 [ i ] ;
}
}
if ( ! color5_delta_hist0 . get_total ( ) ) color5_delta_hist0 . inc ( 0 ) ;
if ( ! color5_delta_hist1 . get_total ( ) ) color5_delta_hist1 . inc ( 0 ) ;
if ( ! color5_delta_hist2 . get_total ( ) ) color5_delta_hist2 . inc ( 0 ) ;
huffman_encoding_table color5_delta_model0 , color5_delta_model1 , color5_delta_model2 , inten_delta_model ;
if ( ! color5_delta_model0 . init ( color5_delta_hist0 , 16 ) )
{
error_printf ( " color5_delta_model.init() failed! " ) ;
return false ;
}
if ( ! color5_delta_model1 . init ( color5_delta_hist1 , 16 ) )
{
error_printf ( " color5_delta_model.init() failed! " ) ;
return false ;
}
if ( ! color5_delta_model2 . init ( color5_delta_hist2 , 16 ) )
{
error_printf ( " color5_delta_model.init() failed! " ) ;
return false ;
}
if ( ! inten_delta_model . init ( inten_delta_hist , 16 ) )
{
error_printf ( " inten3_model.init() failed! " ) ;
return false ;
}
bitwise_coder coder ;
coder . init ( 8192 ) ;
coder . emit_huffman_table ( color5_delta_model0 ) ;
coder . emit_huffman_table ( color5_delta_model1 ) ;
coder . emit_huffman_table ( color5_delta_model2 ) ;
coder . emit_huffman_table ( inten_delta_model ) ;
coder . put_bits ( is_grayscale , 1 ) ;
prev_color5 . set ( 16 , 16 , 16 , 0 ) ;
prev_inten = 0 ;
for ( uint32_t new_endpoint_index = 0 ; new_endpoint_index < r . get_total_endpoint_clusters ( ) ; new_endpoint_index + + )
{
const uint32_t old_endpoint_index = endpoint_remap_table_inv [ new_endpoint_index ] ;
int delta_inten = ( m_endpoint_palette [ old_endpoint_index ] . m_inten5 - prev_inten ) & 7 ;
coder . put_code ( delta_inten , inten_delta_model ) ;
prev_inten = m_endpoint_palette [ old_endpoint_index ] . m_inten5 ;
for ( uint32_t i = 0 ; i < ( is_grayscale ? 1U : 3U ) ; i + + )
{
const int delta = ( m_endpoint_palette [ old_endpoint_index ] . m_color5 [ i ] - prev_color5 [ i ] ) & 31 ;
if ( prev_color5 [ i ] < = basist : : COLOR5_PAL0_PREV_HI )
coder . put_code ( delta , color5_delta_model0 ) ;
else if ( prev_color5 [ i ] < = basist : : COLOR5_PAL1_PREV_HI )
coder . put_code ( delta , color5_delta_model1 ) ;
else
coder . put_code ( delta , color5_delta_model2 ) ;
prev_color5 [ i ] = m_endpoint_palette [ old_endpoint_index ] . m_color5 [ i ] ;
}
} // q
coder . flush ( ) ;
m_output . m_endpoint_palette = coder . get_bytes ( ) ;
debug_printf ( " Endpoint codebook size: %u bits %u bytes, Bits per entry: %3.1f, Avg bits/texel: %3.3f \n " ,
8 * ( int ) m_output . m_endpoint_palette . size ( ) , ( int ) m_output . m_endpoint_palette . size ( ) , m_output . m_endpoint_palette . size ( ) * 8.0f / r . get_total_endpoint_clusters ( ) , m_output . m_endpoint_palette . size ( ) * 8.0f / get_total_input_texels ( ) ) ;
return true ;
}
bool basisu_backend : : encode_selector_palette ( )
{
const basisu_frontend & r = * m_pFront_end ;
if ( ( m_params . m_use_global_sel_codebook ) & & ( ! m_params . m_use_hybrid_sel_codebooks ) )
{
histogram global_mod_indices ( 1 < < m_params . m_global_sel_codebook_mod_bits ) ;
for ( uint32_t q = 0 ; q < r . get_total_selector_clusters ( ) ; q + + )
global_mod_indices . inc ( m_global_selector_palette_desc [ q ] . m_mod_index ) ;
huffman_encoding_table global_pal_model , global_mod_model ;
if ( ! global_mod_model . init ( global_mod_indices , 16 ) )
{
error_printf ( " global_mod_model.init() failed! " ) ;
return false ;
}
bitwise_coder coder ;
coder . init ( 1024 * 1024 ) ;
coder . put_bits ( 1 , 1 ) ; // use global codebook
coder . put_bits ( m_params . m_global_sel_codebook_pal_bits , 4 ) ; // pal bits
coder . put_bits ( m_params . m_global_sel_codebook_mod_bits , 4 ) ; // mod bits
uint32_t mod_model_bits = 0 ;
if ( m_params . m_global_sel_codebook_mod_bits )
mod_model_bits = coder . emit_huffman_table ( global_mod_model ) ;
uint32_t total_pal_bits = 0 ;
uint32_t total_mod_bits = 0 ;
for ( uint32_t q = 0 ; q < r . get_total_selector_clusters ( ) ; q + + )
{
const uint32_t i = m_selector_remap_table_new_to_old [ q ] ;
if ( m_params . m_global_sel_codebook_pal_bits )
{
coder . put_bits ( m_global_selector_palette_desc [ i ] . m_pal_index , m_params . m_global_sel_codebook_pal_bits ) ;
total_pal_bits + = m_params . m_global_sel_codebook_pal_bits ;
}
if ( m_params . m_global_sel_codebook_mod_bits )
total_mod_bits + = coder . put_code ( m_global_selector_palette_desc [ i ] . m_mod_index , global_mod_model ) ;
}
coder . flush ( ) ;
m_output . m_selector_palette = coder . get_bytes ( ) ;
debug_printf ( " Modifier model bits: %u Avg per entry: %3.3f \n " , mod_model_bits , mod_model_bits / float ( r . get_total_selector_clusters ( ) ) ) ;
debug_printf ( " Palette bits: %u Avg per entry: %3.3f, Modifier bits: %u Avg per entry: %3.3f \n " , total_pal_bits , total_pal_bits / float ( r . get_total_selector_clusters ( ) ) , total_mod_bits , total_mod_bits / float ( r . get_total_selector_clusters ( ) ) ) ;
}
else if ( m_params . m_use_hybrid_sel_codebooks )
{
huff2D used_global_cb_bitflag_huff2D ( 1 , 8 ) ;
histogram global_mod_indices ( 1 < < m_params . m_global_sel_codebook_mod_bits ) ;
for ( uint32_t s = 0 ; s < r . get_total_selector_clusters ( ) ; s + + )
{
const uint32_t q = m_selector_remap_table_new_to_old [ s ] ;
const bool used_global_cb_flag = r . get_selector_cluster_uses_global_cb_vec ( ) [ q ] ;
used_global_cb_bitflag_huff2D . emit ( used_global_cb_flag ) ;
global_mod_indices . inc ( m_global_selector_palette_desc [ q ] . m_mod_index ) ;
}
huffman_encoding_table global_mod_indices_model ;
if ( ! global_mod_indices_model . init ( global_mod_indices , 16 ) )
{
error_printf ( " global_mod_indices_model.init() failed! " ) ;
return false ;
}
bitwise_coder coder ;
coder . init ( 1024 * 1024 ) ;
coder . put_bits ( 0 , 1 ) ; // use global codebook
coder . put_bits ( 1 , 1 ) ; // uses hybrid codebooks
coder . put_bits ( m_params . m_global_sel_codebook_pal_bits , 4 ) ; // pal bits
coder . put_bits ( m_params . m_global_sel_codebook_mod_bits , 4 ) ; // mod bits
used_global_cb_bitflag_huff2D . start_encoding ( 16 ) ;
coder . emit_huffman_table ( used_global_cb_bitflag_huff2D . get_encoding_table ( ) ) ;
if ( m_params . m_global_sel_codebook_mod_bits )
coder . emit_huffman_table ( global_mod_indices_model ) ;
uint32_t total_global_cb_entries = 0 ;
uint32_t total_pal_bits = 0 ;
uint32_t total_mod_bits = 0 ;
uint32_t total_selectors = 0 ;
uint32_t total_selector_bits = 0 ;
uint32_t total_flag_bits = 0 ;
for ( uint32_t s = 0 ; s < r . get_total_selector_clusters ( ) ; s + + )
{
const uint32_t q = m_selector_remap_table_new_to_old [ s ] ;
total_flag_bits + = used_global_cb_bitflag_huff2D . emit_next_sym ( coder ) ;
const bool used_global_cb_flag = r . get_selector_cluster_uses_global_cb_vec ( ) [ q ] ;
if ( used_global_cb_flag )
{
total_global_cb_entries + + ;
total_pal_bits + = coder . put_bits ( r . get_selector_cluster_global_selector_entry_ids ( ) [ q ] . m_palette_index , m_params . m_global_sel_codebook_pal_bits ) ;
total_mod_bits + = coder . put_code ( r . get_selector_cluster_global_selector_entry_ids ( ) [ q ] . m_modifier . get_index ( ) , global_mod_indices_model ) ;
}
else
{
total_selectors + + ;
total_selector_bits + = 32 ;
for ( uint32_t j = 0 ; j < 4 ; j + + )
coder . put_bits ( m_selector_palette [ q ] . get_byte ( j ) , 8 ) ;
}
}
coder . flush ( ) ;
m_output . m_selector_palette = coder . get_bytes ( ) ;
debug_printf ( " Total global CB entries: %u %3.2f%% \n " , total_global_cb_entries , total_global_cb_entries * 100.0f / r . get_total_selector_clusters ( ) ) ;
debug_printf ( " Total selector entries: %u %3.2f%% \n " , total_selectors , total_selectors * 100.0f / r . get_total_selector_clusters ( ) ) ;
debug_printf ( " Total pal bits: %u, mod bits: %u, selector bits: %u, flag bits: %u \n " , total_pal_bits , total_mod_bits , total_selector_bits , total_flag_bits ) ;
}
else
{
histogram delta_selector_pal_histogram ( 256 ) ;
for ( uint32_t q = 0 ; q < r . get_total_selector_clusters ( ) ; q + + )
{
if ( ! q )
continue ;
const basist : : etc1_selector_palette_entry & cur = m_selector_palette [ m_selector_remap_table_new_to_old [ q ] ] ;
const basist : : etc1_selector_palette_entry predictor ( m_selector_palette [ m_selector_remap_table_new_to_old [ q - 1 ] ] ) ;
for ( uint32_t j = 0 ; j < 4 ; j + + )
delta_selector_pal_histogram . inc ( cur . get_byte ( j ) ^ predictor . get_byte ( j ) ) ;
}
if ( ! delta_selector_pal_histogram . get_total ( ) )
delta_selector_pal_histogram . inc ( 0 ) ;
huffman_encoding_table delta_selector_pal_model ;
if ( ! delta_selector_pal_model . init ( delta_selector_pal_histogram , 16 ) )
{
error_printf ( " delta_selector_pal_model.init() failed! " ) ;
return false ;
}
bitwise_coder coder ;
coder . init ( 1024 * 1024 ) ;
coder . put_bits ( 0 , 1 ) ; // use global codebook
coder . put_bits ( 0 , 1 ) ; // uses hybrid codebooks
coder . put_bits ( 0 , 1 ) ; // raw bytes
coder . emit_huffman_table ( delta_selector_pal_model ) ;
for ( uint32_t q = 0 ; q < r . get_total_selector_clusters ( ) ; q + + )
{
if ( ! q )
{
for ( uint32_t j = 0 ; j < 4 ; j + + )
coder . put_bits ( m_selector_palette [ m_selector_remap_table_new_to_old [ q ] ] . get_byte ( j ) , 8 ) ;
continue ;
}
const basist : : etc1_selector_palette_entry & cur = m_selector_palette [ m_selector_remap_table_new_to_old [ q ] ] ;
const basist : : etc1_selector_palette_entry predictor ( m_selector_palette [ m_selector_remap_table_new_to_old [ q - 1 ] ] ) ;
for ( uint32_t j = 0 ; j < 4 ; j + + )
coder . put_code ( cur . get_byte ( j ) ^ predictor . get_byte ( j ) , delta_selector_pal_model ) ;
}
coder . flush ( ) ;
m_output . m_selector_palette = coder . get_bytes ( ) ;
if ( m_output . m_selector_palette . size ( ) > = r . get_total_selector_clusters ( ) * 4 )
{
coder . init ( 1024 * 1024 ) ;
coder . put_bits ( 0 , 1 ) ; // use global codebook
coder . put_bits ( 0 , 1 ) ; // uses hybrid codebooks
coder . put_bits ( 1 , 1 ) ; // raw bytes
for ( uint32_t q = 0 ; q < r . get_total_selector_clusters ( ) ; q + + )
{
const uint32_t i = m_selector_remap_table_new_to_old [ q ] ;
for ( uint32_t j = 0 ; j < 4 ; j + + )
coder . put_bits ( m_selector_palette [ i ] . get_byte ( j ) , 8 ) ;
}
coder . flush ( ) ;
m_output . m_selector_palette = coder . get_bytes ( ) ;
}
} // if (m_params.m_use_global_sel_codebook)
debug_printf ( " Selector codebook bits: %u bytes: %u, Bits per entry: %3.1f, Avg bits/texel: %3.3f \n " ,
( int ) m_output . m_selector_palette . size ( ) * 8 , ( int ) m_output . m_selector_palette . size ( ) ,
m_output . m_selector_palette . size ( ) * 8.0f / r . get_total_selector_clusters ( ) , m_output . m_selector_palette . size ( ) * 8.0f / get_total_input_texels ( ) ) ;
return true ;
}
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 ( ) ;
create_encoder_blocks ( ) ;
if ( ! encode_image ( ) )
return 0 ;
if ( ! encode_endpoint_palette ( ) )
return 0 ;
if ( ! encode_selector_palette ( ) )
return 0 ;
uint32_t total_compressed_bytes = ( uint32_t ) ( m_output . m_slice_image_tables . size ( ) + m_output . m_endpoint_palette . size ( ) + m_output . m_selector_palette . size ( ) ) ;
for ( uint32_t i = 0 ; i < m_output . m_slice_image_data . size ( ) ; i + + )
total_compressed_bytes + = ( uint32_t ) m_output . m_slice_image_data [ i ] . size ( ) ;
debug_printf ( " Wrote %u bytes, %3.3f bits/texel \n " , total_compressed_bytes , total_compressed_bytes * 8.0f / get_total_input_texels ( ) ) ;
return total_compressed_bytes ;
}
} // namespace basisu