xenia/third_party/crunch/crnlib/crn_dxt1.h

353 lines
11 KiB
C++

// File: crn_dxt1.h
// See Copyright Notice and license at the end of inc/crnlib.h
#pragma once
#include "crn_dxt.h"
namespace crnlib
{
struct dxt1_solution_coordinates
{
inline dxt1_solution_coordinates() : m_low_color(0), m_high_color(0){ }
inline dxt1_solution_coordinates(uint16 l, uint16 h) : m_low_color(l), m_high_color(h) { }
inline dxt1_solution_coordinates(const color_quad_u8& l, const color_quad_u8& h, bool scaled = true) :
m_low_color(dxt1_block::pack_color(l, scaled)),
m_high_color(dxt1_block::pack_color(h, scaled))
{
}
inline dxt1_solution_coordinates(vec3F nl, vec3F nh)
{
#if CRNLIB_DXT_ALT_ROUNDING
// Umm, wtf?
nl.clamp(0.0f, .999f);
nh.clamp(0.0f, .999f);
color_quad_u8 l( (int)floor(nl[0] * 32.0f), (int)floor(nl[1] * 64.0f), (int)floor(nl[2] * 32.0f), 255);
color_quad_u8 h( (int)floor(nh[0] * 32.0f), (int)floor(nh[1] * 64.0f), (int)floor(nh[2] * 32.0f), 255);
#else
// Fixes the bins
color_quad_u8 l( (int)floor(.5f + nl[0] * 31.0f), (int)floor(.5f + nl[1] * 63.0f), (int)floor(.5f + nl[2] * 31.0f), 255);
color_quad_u8 h( (int)floor(.5f + nh[0] * 31.0f), (int)floor(.5f + nh[1] * 63.0f), (int)floor(.5f + nh[2] * 31.0f), 255);
#endif
m_low_color = dxt1_block::pack_color(l, false);
m_high_color = dxt1_block::pack_color(h, false);
}
uint16 m_low_color;
uint16 m_high_color;
inline void clear()
{
m_low_color = 0;
m_high_color = 0;
}
inline dxt1_solution_coordinates& canonicalize()
{
if (m_low_color < m_high_color)
utils::swap(m_low_color, m_high_color);
return *this;
}
inline operator size_t() const { return fast_hash(this, sizeof(*this)); }
inline bool operator== (const dxt1_solution_coordinates& other) const
{
uint16 l0 = math::minimum(m_low_color, m_high_color);
uint16 h0 = math::maximum(m_low_color, m_high_color);
uint16 l1 = math::minimum(other.m_low_color, other.m_high_color);
uint16 h1 = math::maximum(other.m_low_color, other.m_high_color);
return (l0 == l1) && (h0 == h1);
}
inline bool operator!= (const dxt1_solution_coordinates& other) const
{
return !(*this == other);
}
inline bool operator< (const dxt1_solution_coordinates& other) const
{
uint16 l0 = math::minimum(m_low_color, m_high_color);
uint16 h0 = math::maximum(m_low_color, m_high_color);
uint16 l1 = math::minimum(other.m_low_color, other.m_high_color);
uint16 h1 = math::maximum(other.m_low_color, other.m_high_color);
if (l0 < l1)
return true;
else if (l0 == l1)
{
if (h0 < h1)
return true;
}
return false;
}
};
typedef crnlib::vector<dxt1_solution_coordinates> dxt1_solution_coordinates_vec;
CRNLIB_DEFINE_BITWISE_COPYABLE(dxt1_solution_coordinates);
struct unique_color
{
inline unique_color() { }
inline unique_color(const color_quad_u8& color, uint weight) : m_color(color), m_weight(weight) { }
color_quad_u8 m_color;
uint m_weight;
inline bool operator< (const unique_color& c) const
{
return *reinterpret_cast<const uint32*>(&m_color) < *reinterpret_cast<const uint32*>(&c.m_color);
}
inline bool operator== (const unique_color& c) const
{
return *reinterpret_cast<const uint32*>(&m_color) == *reinterpret_cast<const uint32*>(&c.m_color);
}
};
CRNLIB_DEFINE_BITWISE_COPYABLE(unique_color);
class dxt1_endpoint_optimizer
{
public:
dxt1_endpoint_optimizer();
struct params
{
params() :
m_block_index(0),
m_pPixels(NULL),
m_num_pixels(0),
m_dxt1a_alpha_threshold(128U),
m_quality(cCRNDXTQualityUber),
m_pixels_have_alpha(false),
m_use_alpha_blocks(true),
m_perceptual(true),
m_grayscale_sampling(false),
m_endpoint_caching(true),
m_use_transparent_indices_for_black(false),
m_force_alpha_blocks(false)
{
m_color_weights[0] = 1;
m_color_weights[1] = 1;
m_color_weights[2] = 1;
}
uint m_block_index;
const color_quad_u8* m_pPixels;
uint m_num_pixels;
uint m_dxt1a_alpha_threshold;
crn_dxt_quality m_quality;
bool m_pixels_have_alpha;
bool m_use_alpha_blocks;
bool m_perceptual;
bool m_grayscale_sampling;
bool m_endpoint_caching;
bool m_use_transparent_indices_for_black;
bool m_force_alpha_blocks;
int m_color_weights[3];
};
struct results
{
inline results() : m_pSelectors(NULL) { }
uint64 m_error;
uint16 m_low_color;
uint16 m_high_color;
uint8* m_pSelectors;
bool m_alpha_block;
};
struct solution
{
solution() { }
solution(const solution& other)
{
m_results = other.m_results;
m_selectors = other.m_selectors;
m_results.m_pSelectors = m_selectors.begin();
}
solution& operator= (const solution& rhs)
{
if (this == &rhs)
return *this;
m_results = rhs.m_results;
m_selectors = rhs.m_selectors;
m_results.m_pSelectors = m_selectors.begin();
return *this;
}
results m_results;
crnlib::vector<uint8> m_selectors;
inline bool operator< (const solution& other) const
{
return m_results.m_error < other.m_results.m_error;
}
static inline bool coords_equal(const solution& lhs, const solution& rhs)
{
return (lhs.m_results.m_low_color == rhs.m_results.m_low_color) && (lhs.m_results.m_high_color == rhs.m_results.m_high_color);
}
};
typedef crnlib::vector<solution> solution_vec;
bool compute(const params& p, results& r, solution_vec* pSolutions = NULL);
private:
const params* m_pParams;
results* m_pResults;
solution_vec* m_pSolutions;
bool m_perceptual;
bool m_has_color_weighting;
typedef crnlib::vector<unique_color> unique_color_vec;
//typedef crnlib::hash_map<uint32, uint32, bit_hasher<uint32> > unique_color_hash_map;
typedef crnlib::hash_map<uint32, uint32> unique_color_hash_map;
unique_color_hash_map m_unique_color_hash_map;
unique_color_vec m_unique_colors; // excludes transparent colors!
unique_color_vec m_temp_unique_colors;
uint m_total_unique_color_weight;
bool m_has_transparent_pixels;
vec3F_array m_norm_unique_colors;
vec3F m_mean_norm_color;
vec3F_array m_norm_unique_colors_weighted;
vec3F m_mean_norm_color_weighted;
vec3F m_principle_axis;
bool m_all_pixels_grayscale;
crnlib::vector<uint16> m_unique_packed_colors;
crnlib::vector<uint8> m_trial_selectors;
crnlib::vector<vec3F> m_low_coords;
crnlib::vector<vec3F> m_high_coords;
enum { cMaxPrevResults = 4 };
dxt1_solution_coordinates m_prev_results[cMaxPrevResults];
uint m_num_prev_results;
crnlib::vector<vec3I> m_lo_cells;
crnlib::vector<vec3I> m_hi_cells;
uint m_total_evals;
struct potential_solution
{
potential_solution() : m_coords(), m_error(cUINT64_MAX), m_alpha_block(false), m_valid(false)
{
}
dxt1_solution_coordinates m_coords;
crnlib::vector<uint8> m_selectors;
uint64 m_error;
bool m_alpha_block;
bool m_valid;
void clear()
{
m_coords.clear();
m_selectors.resize(0);
m_error = cUINT64_MAX;
m_alpha_block = false;
m_valid = false;
}
bool are_selectors_all_equal() const
{
if (m_selectors.empty())
return false;
const uint s = m_selectors[0];
for (uint i = 1; i < m_selectors.size(); i++)
if (m_selectors[i] != s)
return false;
return true;
}
};
potential_solution m_trial_solution;
potential_solution m_best_solution;
typedef crnlib::hash_map<uint, empty_type> solution_hash_map;
solution_hash_map m_solutions_tried;
bool refine_solution(int refinement_level = 0);
bool evaluate_solution(
const dxt1_solution_coordinates& coords,
bool early_out,
potential_solution* pBest_solution,
bool alternate_rounding = false);
bool evaluate_solution_uber(
potential_solution& solution,
const dxt1_solution_coordinates& coords,
bool early_out,
potential_solution* pBest_solution,
bool alternate_rounding = false);
bool evaluate_solution_fast(
potential_solution& solution,
const dxt1_solution_coordinates& coords,
bool early_out,
potential_solution* pBest_solution,
bool alternate_rounding = false);
void clear();
void find_unique_colors();
bool handle_all_transparent_block();
bool handle_solid_block();
bool handle_multicolor_block();
bool handle_grayscale_block();
void compute_pca(vec3F& axis, const vec3F_array& norm_colors, const vec3F& def);
void compute_vectors(const vec3F& perceptual_weights);
void return_solution(results& results, const potential_solution& solution);
void try_combinatorial_encoding();
void optimize_endpoint_comps();
bool optimize_endpoints(vec3F& low_color, vec3F& high_color);
bool try_alpha_as_black_optimization();
bool try_average_block_as_solid();
bool try_median4(const vec3F& low_color, const vec3F& high_color);
bool compute_internal(const params& p, results& r, solution_vec* pSolutions);
unique_color lerp_color(const color_quad_u8& a, const color_quad_u8& b, float f, int rounding = 1);
inline uint color_distance(bool perceptual, const color_quad_u8& e1, const color_quad_u8& e2, bool alpha);
static inline vec3F unpack_to_vec3F_raw(uint16 packed_color);
static inline vec3F unpack_to_vec3F(uint16 packed_color);
};
inline void swap(dxt1_endpoint_optimizer::solution& a, dxt1_endpoint_optimizer::solution& b)
{
std::swap(a.m_results, b.m_results);
a.m_selectors.swap(b.m_selectors);
}
} // namespace crnlib