974fab40a5
This change improves the compression ratio. Explanation: In the original version of Crunch all the blocks are grouped into chunks of 2x2 blocks. Each chunk can have one of 8 different types. The type of the chunk determines which blocks inside the chunk share the same endpoints (for example, all the blocks inside the chunk share the same endpoints, or blocks in the right column share the same endpoints, or all the blocks have different endpoints, etc.). Encoding of endpoints equality is usually cheaper than encoding of duplicate endpoint indices. The used 8 chunk types do not cover all the possibilities, but they can be efficiently encoded using 0.75 bits per block (uncompressed). The modified algorithm no longer uses the concept of chunks in the output file format and is based on an alternative approach. Endpoints for each block can be either copied from the left nearest block (reference to the left), copied from the upper nearest block (reference to the top), or decoded from the stream (reference to itself). Note that this is a superset of the original encoding, so all the images previously encoded with the original algorithm can be losslessly transcoded into the new format, but not vice versa. Even though the new endpoint equality encoding is more expensive (about 1.58 bits per block, uncompressed), it provides more flexibility for endpoint matching inside the former "chunks", and more importantly, it allows to inherit endpoints from outside the former "chunks" (which is not possible when using the original chunk encoding). The blocks are no longer grouped together and are encoded in the same order as they appear on the image. Note: This modification alters the output file format and makes it incompatible with the previous revisions. Testing: The modified algorithm has been tested on the Kodak test set using 64-bit build with default settings (running on Windows 10, i7-4790, 3.6GHz). All the decompressed test images are identical to the images being compressed and decompressed using original version of Crunch. [Compressing Kodak set without mipmaps] Original: 1582222 bytes / 28.903 sec Modified: 1548791 bytes / 28.818 sec Improvement: 2.11% (compression ratio) / 0.29% (compression time) [Compressing Kodak set with mipmaps] Original: 2065243 bytes / 36.978 sec Modified: 2017245 bytes / 36.846 sec Improvement: 2.32% (compression ratio) / 0.36% (compression time)
172 lines
5.8 KiB
C++
172 lines
5.8 KiB
C++
// File: crn_comp.h
|
|
// See Copyright Notice and license at the end of inc/crnlib.h
|
|
#pragma once
|
|
|
|
#include "../inc/crn_defs.h"
|
|
|
|
#include "../inc/crnlib.h"
|
|
#include "crn_symbol_codec.h"
|
|
#include "crn_dxt_hc.h"
|
|
#include "crn_image.h"
|
|
#include "crn_image_utils.h"
|
|
#include "crn_texture_comp.h"
|
|
|
|
namespace crnlib {
|
|
class crn_comp : public itexture_comp {
|
|
CRNLIB_NO_COPY_OR_ASSIGNMENT_OP(crn_comp);
|
|
|
|
public:
|
|
crn_comp();
|
|
virtual ~crn_comp();
|
|
|
|
virtual const char* get_ext() const { return "CRN"; }
|
|
|
|
virtual bool compress_init(const crn_comp_params& params);
|
|
virtual bool compress_pass(const crn_comp_params& params, float* pEffective_bitrate);
|
|
virtual void compress_deinit();
|
|
|
|
virtual const crnlib::vector<uint8>& get_comp_data() const { return m_comp_data; }
|
|
virtual crnlib::vector<uint8>& get_comp_data() { return m_comp_data; }
|
|
|
|
uint get_comp_data_size() const { return m_comp_data.size(); }
|
|
const uint8* get_comp_data_ptr() const { return m_comp_data.size() ? &m_comp_data[0] : NULL; }
|
|
|
|
private:
|
|
task_pool m_task_pool;
|
|
const crn_comp_params* m_pParams;
|
|
|
|
image_u8 m_images[cCRNMaxFaces][cCRNMaxLevels];
|
|
|
|
struct level_tag {
|
|
uint m_width, m_height;
|
|
uint m_chunk_width, m_chunk_height;
|
|
uint m_group_index;
|
|
uint m_num_chunks;
|
|
uint m_first_chunk;
|
|
uint m_group_first_chunk;
|
|
} m_levels[cCRNMaxLevels];
|
|
|
|
struct mip_group {
|
|
mip_group()
|
|
: m_first_chunk(0), m_num_chunks(0) {}
|
|
|
|
uint m_first_chunk;
|
|
uint m_num_chunks;
|
|
uint m_chunk_width;
|
|
};
|
|
crnlib::vector<mip_group> m_mip_groups;
|
|
|
|
enum comp {
|
|
cColor,
|
|
cAlpha0,
|
|
cAlpha1,
|
|
cNumComps
|
|
};
|
|
|
|
bool m_has_comp[cNumComps];
|
|
|
|
struct chunk_detail {
|
|
chunk_detail() { utils::zero_object(*this); }
|
|
uint16 m_endpoint_indices[2][2][cNumComps];
|
|
uint16 m_selector_indices[2][2][cNumComps];
|
|
uint8 m_endpoint_references[2][2];
|
|
uint8 m_reference_group;
|
|
};
|
|
crnlib::vector<chunk_detail> m_chunk_details;
|
|
|
|
uint m_total_chunks;
|
|
dxt_hc::pixel_chunk_vec m_chunks;
|
|
|
|
crnd::crn_header m_crn_header;
|
|
crnlib::vector<uint8> m_comp_data;
|
|
|
|
dxt_hc m_hvq;
|
|
|
|
symbol_histogram m_chunk_encoding_hist;
|
|
static_huffman_data_model m_reference_encoding_dm;
|
|
|
|
symbol_histogram m_endpoint_index_hist[2];
|
|
static_huffman_data_model m_endpoint_index_dm[2]; // color, alpha
|
|
|
|
symbol_histogram m_selector_index_hist[2];
|
|
static_huffman_data_model m_selector_index_dm[2]; // color, alpha
|
|
|
|
crnlib::vector<uint8> m_packed_chunks[cCRNMaxLevels];
|
|
crnlib::vector<uint8> m_packed_data_models;
|
|
crnlib::vector<uint8> m_packed_color_endpoints;
|
|
crnlib::vector<uint8> m_packed_color_selectors;
|
|
crnlib::vector<uint8> m_packed_alpha_endpoints;
|
|
crnlib::vector<uint8> m_packed_alpha_selectors;
|
|
|
|
void clear();
|
|
|
|
void append_chunks(const image_u8& img, uint num_chunks_x, uint num_chunks_y, dxt_hc::pixel_chunk_vec& chunks, float weight);
|
|
|
|
static float color_endpoint_similarity_func(uint index_a, uint index_b, void* pContext);
|
|
static float alpha_endpoint_similarity_func(uint index_a, uint index_b, void* pContext);
|
|
void sort_color_endpoint_codebook(crnlib::vector<uint>& remapping, const crnlib::vector<uint>& endpoints);
|
|
void sort_alpha_endpoint_codebook(crnlib::vector<uint>& remapping, const crnlib::vector<uint>& endpoints);
|
|
|
|
bool pack_color_endpoints(crnlib::vector<uint8>& data, const crnlib::vector<uint>& remapping, uint trial_index);
|
|
bool pack_alpha_endpoints(crnlib::vector<uint8>& data, const crnlib::vector<uint>& remapping, uint trial_index);
|
|
|
|
static float color_selector_similarity_func(uint index_a, uint index_b, void* pContext);
|
|
static float alpha_selector_similarity_func(uint index_a, uint index_b, void* pContext);
|
|
void sort_selector_codebook(crnlib::vector<uint>& remapping, const crnlib::vector<dxt_hc::selectors>& selectors, const uint8* pTo_linear);
|
|
|
|
bool pack_selectors(
|
|
crnlib::vector<uint8>& packed_data,
|
|
const crnlib::vector<dxt_hc::selectors>& selectors,
|
|
const crnlib::vector<uint>& remapping,
|
|
uint max_selector_value,
|
|
const uint8* pTo_linear,
|
|
uint trial_index);
|
|
|
|
bool alias_images();
|
|
void create_chunks();
|
|
bool quantize_chunks();
|
|
void create_chunk_indices();
|
|
|
|
bool pack_chunks(
|
|
uint group,
|
|
bool clear_histograms,
|
|
symbol_codec* pCodec,
|
|
const crnlib::vector<uint>* pColor_endpoint_remap,
|
|
const crnlib::vector<uint>* pColor_selector_remap,
|
|
const crnlib::vector<uint>* pAlpha_endpoint_remap,
|
|
const crnlib::vector<uint>* pAlpha_selector_remap);
|
|
|
|
bool pack_chunks_simulation(
|
|
uint first_chunk, uint num_chunks,
|
|
uint& total_bits,
|
|
const crnlib::vector<uint>* pColor_endpoint_remap,
|
|
const crnlib::vector<uint>* pColor_selector_remap,
|
|
const crnlib::vector<uint>* pAlpha_endpoint_remap,
|
|
const crnlib::vector<uint>* pAlpha_selector_remap);
|
|
|
|
void optimize_color_endpoint_codebook_task(uint64 data, void* pData_ptr);
|
|
bool optimize_color_endpoint_codebook(crnlib::vector<uint>& remapping);
|
|
|
|
void optimize_color_selector_codebook_task(uint64 data, void* pData_ptr);
|
|
bool optimize_color_selector_codebook(crnlib::vector<uint>& remapping);
|
|
|
|
void optimize_alpha_endpoint_codebook_task(uint64 data, void* pData_ptr);
|
|
bool optimize_alpha_endpoint_codebook(crnlib::vector<uint>& remapping);
|
|
|
|
void optimize_alpha_selector_codebook_task(uint64 data, void* pData_ptr);
|
|
bool optimize_alpha_selector_codebook(crnlib::vector<uint>& remapping);
|
|
|
|
bool create_comp_data();
|
|
|
|
bool pack_data_models();
|
|
|
|
bool update_progress(uint phase_index, uint subphase_index, uint subphase_total);
|
|
|
|
bool compress_internal();
|
|
|
|
static void append_vec(crnlib::vector<uint8>& a, const void* p, uint size);
|
|
static void append_vec(crnlib::vector<uint8>& a, const crnlib::vector<uint8>& b);
|
|
};
|
|
|
|
} // namespace crnlib
|