249 lines
9.8 KiB
C
249 lines
9.8 KiB
C
|
// File: crn_dxt_image.h
|
||
|
// See Copyright Notice and license at the end of inc/crnlib.h
|
||
|
#pragma once
|
||
|
#include "crn_dxt1.h"
|
||
|
#include "crn_dxt5a.h"
|
||
|
#include "crn_etc.h"
|
||
|
#if CRNLIB_SUPPORT_ETC_A1
|
||
|
#include "crn_etc_a1.h"
|
||
|
#endif
|
||
|
#include "crn_image.h"
|
||
|
|
||
|
#define CRNLIB_SUPPORT_ATI_COMPRESS 0
|
||
|
|
||
|
namespace crnlib
|
||
|
{
|
||
|
class task_pool;
|
||
|
|
||
|
class dxt_image
|
||
|
{
|
||
|
public:
|
||
|
dxt_image();
|
||
|
dxt_image(const dxt_image& other);
|
||
|
dxt_image& operator= (const dxt_image& rhs);
|
||
|
|
||
|
void clear();
|
||
|
|
||
|
inline bool is_valid() const { return m_blocks_x > 0; }
|
||
|
|
||
|
uint get_width() const { return m_width; }
|
||
|
uint get_height() const { return m_height; }
|
||
|
|
||
|
uint get_blocks_x() const { return m_blocks_x; }
|
||
|
uint get_blocks_y() const { return m_blocks_y; }
|
||
|
uint get_total_blocks() const { return m_blocks_x * m_blocks_y; }
|
||
|
|
||
|
uint get_elements_per_block() const { return m_num_elements_per_block; }
|
||
|
uint get_bytes_per_block() const { return m_bytes_per_block; }
|
||
|
|
||
|
dxt_format get_format() const { return m_format; }
|
||
|
|
||
|
bool has_color() const { return (m_format == cDXT1) || (m_format == cDXT1A) || (m_format == cDXT3) || (m_format == cDXT5) || (m_format == cETC1); }
|
||
|
|
||
|
// Will be pretty slow if the image is DXT1, as this method scans for alpha blocks/selectors.
|
||
|
bool has_alpha() const;
|
||
|
|
||
|
enum element_type
|
||
|
{
|
||
|
cUnused = 0,
|
||
|
|
||
|
cColorDXT1, // DXT1 color block
|
||
|
|
||
|
cAlphaDXT3, // DXT3 alpha block (only)
|
||
|
cAlphaDXT5, // DXT5 alpha block (only)
|
||
|
|
||
|
cColorETC1, // ETC1 color block
|
||
|
};
|
||
|
|
||
|
element_type get_element_type(uint element_index) const { CRNLIB_ASSERT(element_index < m_num_elements_per_block); return m_element_type[element_index]; }
|
||
|
|
||
|
//Returns -1 for RGB, or [0,3]
|
||
|
int8 get_element_component_index(uint element_index) const { CRNLIB_ASSERT(element_index < m_num_elements_per_block); return m_element_component_index[element_index]; }
|
||
|
|
||
|
struct element
|
||
|
{
|
||
|
uint8 m_bytes[8];
|
||
|
|
||
|
uint get_le_word(uint index) const { CRNLIB_ASSERT(index < 4); return m_bytes[index*2] | (m_bytes[index * 2 + 1] << 8); }
|
||
|
uint get_be_word(uint index) const { CRNLIB_ASSERT(index < 4); return m_bytes[index*2 + 1] | (m_bytes[index * 2] << 8); }
|
||
|
|
||
|
void set_le_word(uint index, uint val) { CRNLIB_ASSERT((index < 4) && (val <= cUINT16_MAX)); m_bytes[index*2] = static_cast<uint8>(val & 0xFF); m_bytes[index * 2 + 1] = static_cast<uint8>((val >> 8) & 0xFF); }
|
||
|
void set_be_word(uint index, uint val) { CRNLIB_ASSERT((index < 4) && (val <= cUINT16_MAX)); m_bytes[index*2+1] = static_cast<uint8>(val & 0xFF); m_bytes[index * 2] = static_cast<uint8>((val >> 8) & 0xFF); }
|
||
|
|
||
|
void clear()
|
||
|
{
|
||
|
memset(this, 0, sizeof(*this));
|
||
|
}
|
||
|
};
|
||
|
|
||
|
typedef crnlib::vector<element> element_vec;
|
||
|
|
||
|
bool init(dxt_format fmt, uint width, uint height, bool clear_elements);
|
||
|
bool init(dxt_format fmt, uint width, uint height, uint num_elements, element* pElements, bool create_copy);
|
||
|
|
||
|
struct pack_params
|
||
|
{
|
||
|
pack_params()
|
||
|
{
|
||
|
clear();
|
||
|
}
|
||
|
|
||
|
void clear()
|
||
|
{
|
||
|
m_quality = cCRNDXTQualityUber;
|
||
|
m_perceptual = true;
|
||
|
m_dithering = false;
|
||
|
m_grayscale_sampling = false;
|
||
|
m_use_both_block_types = true;
|
||
|
m_endpoint_caching = true;
|
||
|
m_compressor = cCRNDXTCompressorCRN;
|
||
|
m_pProgress_callback = NULL;
|
||
|
m_pProgress_callback_user_data_ptr = NULL;
|
||
|
m_dxt1a_alpha_threshold = 128;
|
||
|
m_num_helper_threads = 0;
|
||
|
m_progress_start = 0;
|
||
|
m_progress_range = 100;
|
||
|
m_use_transparent_indices_for_black = false;
|
||
|
m_pTask_pool = NULL;
|
||
|
m_color_weights[0] = 1;
|
||
|
m_color_weights[1] = 1;
|
||
|
m_color_weights[2] = 1;
|
||
|
}
|
||
|
|
||
|
void init(const crn_comp_params ¶ms)
|
||
|
{
|
||
|
m_perceptual = (params.m_flags & cCRNCompFlagPerceptual) != 0;
|
||
|
m_num_helper_threads = params.m_num_helper_threads;
|
||
|
m_use_both_block_types = (params.m_flags & cCRNCompFlagUseBothBlockTypes) != 0;
|
||
|
m_use_transparent_indices_for_black = (params.m_flags & cCRNCompFlagUseTransparentIndicesForBlack) != 0;
|
||
|
m_dxt1a_alpha_threshold = params.m_dxt1a_alpha_threshold;
|
||
|
m_quality = params.m_dxt_quality;
|
||
|
m_endpoint_caching = (params.m_flags & cCRNCompFlagDisableEndpointCaching) == 0;
|
||
|
m_grayscale_sampling = (params.m_flags & cCRNCompFlagGrayscaleSampling) != 0;
|
||
|
m_compressor = params.m_dxt_compressor_type;
|
||
|
}
|
||
|
|
||
|
uint m_dxt1a_alpha_threshold;
|
||
|
|
||
|
uint m_num_helper_threads;
|
||
|
|
||
|
crn_dxt_quality m_quality;
|
||
|
|
||
|
crn_dxt_compressor_type m_compressor;
|
||
|
|
||
|
bool m_perceptual;
|
||
|
bool m_dithering;
|
||
|
bool m_grayscale_sampling;
|
||
|
bool m_use_both_block_types;
|
||
|
bool m_endpoint_caching;
|
||
|
bool m_use_transparent_indices_for_black;
|
||
|
|
||
|
typedef bool (*progress_callback_func)(uint percentage_complete, void* pUser_data_ptr);
|
||
|
progress_callback_func m_pProgress_callback;
|
||
|
void* m_pProgress_callback_user_data_ptr;
|
||
|
|
||
|
uint m_progress_start;
|
||
|
uint m_progress_range;
|
||
|
|
||
|
task_pool *m_pTask_pool;
|
||
|
|
||
|
int m_color_weights[3];
|
||
|
};
|
||
|
|
||
|
bool init(dxt_format fmt, const image_u8& img, const pack_params& p = dxt_image::pack_params());
|
||
|
|
||
|
bool unpack(image_u8& img) const;
|
||
|
|
||
|
void endian_swap();
|
||
|
|
||
|
uint get_total_elements() const { return m_elements.size(); }
|
||
|
|
||
|
const element_vec& get_element_vec() const { return m_elements; }
|
||
|
element_vec& get_element_vec() { return m_elements; }
|
||
|
|
||
|
const element& get_element(uint block_x, uint block_y, uint element_index) const;
|
||
|
element& get_element(uint block_x, uint block_y, uint element_index);
|
||
|
|
||
|
const element* get_element_ptr() const { return m_pElements; }
|
||
|
element* get_element_ptr() { return m_pElements; }
|
||
|
|
||
|
uint get_size_in_bytes() const { return m_elements.size() * sizeof(element); }
|
||
|
uint get_row_pitch_in_bytes() const { return m_blocks_x * m_bytes_per_block; }
|
||
|
|
||
|
color_quad_u8 get_pixel(uint x, uint y) const;
|
||
|
uint get_pixel_alpha(uint x, uint y, uint element_index) const;
|
||
|
|
||
|
void set_pixel(uint x, uint y, const color_quad_u8& c, bool perceptual = true);
|
||
|
|
||
|
// get_block_pixels() only sets those components stored in the image!
|
||
|
bool get_block_pixels(uint block_x, uint block_y, color_quad_u8* pPixels) const;
|
||
|
|
||
|
struct set_block_pixels_context
|
||
|
{
|
||
|
dxt1_endpoint_optimizer m_dxt1_optimizer;
|
||
|
dxt5_endpoint_optimizer m_dxt5_optimizer;
|
||
|
pack_etc1_block_context m_etc1_optimizer;
|
||
|
#if CRNLIB_SUPPORT_ETC_A1
|
||
|
etc_a1::pack_etc1_block_context m_etc1_a1_optimizer;
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
void set_block_pixels(uint block_x, uint block_y, const color_quad_u8* pPixels, const pack_params& p, set_block_pixels_context& context);
|
||
|
void set_block_pixels(uint block_x, uint block_y, const color_quad_u8* pPixels, const pack_params& p);
|
||
|
|
||
|
void get_block_endpoints(uint block_x, uint block_y, uint element_index, uint& packed_low_endpoint, uint& packed_high_endpoint) const;
|
||
|
|
||
|
// Returns a value representing the component(s) that where actually set, where -1 = RGB.
|
||
|
// This method does not always set every component!
|
||
|
int get_block_endpoints(uint block_x, uint block_y, uint element_index, color_quad_u8& low_endpoint, color_quad_u8& high_endpoint, bool scaled = true) const;
|
||
|
|
||
|
// pColors should point to a 16 entry array, to handle DXT3.
|
||
|
// Returns the number of block colors: 3, 4, 6, 8, or 16.
|
||
|
uint get_block_colors(uint block_x, uint block_y, uint element_index, color_quad_u8* pColors, uint subblock_index = 0);
|
||
|
|
||
|
uint get_subblock_index(uint x, uint y, uint element_index) const;
|
||
|
uint get_total_subblocks(uint element_index) const;
|
||
|
|
||
|
uint get_selector(uint x, uint y, uint element_index) const;
|
||
|
|
||
|
void change_dxt1_to_dxt1a();
|
||
|
|
||
|
bool can_flip(uint axis_index);
|
||
|
|
||
|
// Returns true if the texture can actually be flipped.
|
||
|
bool flip_x();
|
||
|
bool flip_y();
|
||
|
|
||
|
private:
|
||
|
element_vec m_elements;
|
||
|
element* m_pElements;
|
||
|
|
||
|
uint m_width;
|
||
|
uint m_height;
|
||
|
|
||
|
uint m_blocks_x;
|
||
|
uint m_blocks_y;
|
||
|
uint m_total_blocks;
|
||
|
uint m_total_elements;
|
||
|
|
||
|
uint m_num_elements_per_block; // 1 or 2
|
||
|
uint m_bytes_per_block; // 8 or 16
|
||
|
|
||
|
int8 m_element_component_index[2];
|
||
|
element_type m_element_type[2];
|
||
|
|
||
|
dxt_format m_format; // DXT1, 1A, 3, 5, N/3DC, or 5A
|
||
|
|
||
|
bool init_internal(dxt_format fmt, uint width, uint height);
|
||
|
void init_task(uint64 data, void* pData_ptr);
|
||
|
|
||
|
#if CRNLIB_SUPPORT_ATI_COMPRESS
|
||
|
bool init_ati_compress(dxt_format fmt, const image_u8& img, const pack_params& p);
|
||
|
#endif
|
||
|
|
||
|
void flip_col(uint x);
|
||
|
void flip_row(uint y);
|
||
|
};
|
||
|
|
||
|
} // namespace crnlib
|