Initial checkin of v1.04 - KTX file format support, basic ETC1 compression/decompression, Linux makefile with proper gcc options, lots of high-level improvements to get crnlib into a state where I can more easily add additional formats.

This commit is contained in:
richgel99@gmail.com
2012-11-25 08:41:25 +00:00
parent a8011e9d7f
commit f71b49be60
92 changed files with 20362 additions and 781 deletions
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
+96
View File
@@ -0,0 +1,96 @@
COMPILE_OPTIONS = -O3 -fomit-frame-pointer -ffast-math -fno-math-errno -g -fno-strict-aliasing -Wall -Wno-unused-value -Wno-unused -march=core2
LINKER_OPTIONS = -lpthread -g
OBJECTS = \
crn_arealist.o \
crn_assert.o \
crn_checksum.o \
crn_colorized_console.o \
crn_command_line_params.o \
crn_comp.o \
crn_console.o \
crn_core.o \
crn_data_stream.o \
crn_mipmapped_texture.o \
crn_decomp.o \
crn_dxt1.o \
crn_dxt5a.o \
crn_dxt.o \
crn_dxt_endpoint_refiner.o \
crn_dxt_fast.o \
crn_dxt_hc_common.o \
crn_dxt_hc.o \
crn_dxt_image.o \
crn_dynamic_string.o \
crn_file_utils.o \
crn_find_files.o \
crn_hash.o \
crn_hash_map.o \
crn_huffman_codes.o \
crn_image_utils.o \
crnlib.o \
crn_math.o \
crn_mem.o \
crn_pixel_format.o \
crn_platform.o \
crn_prefix_coding.o \
crn_qdxt1.o \
crn_qdxt5.o \
crn_rand.o \
crn_resample_filters.o \
crn_resampler.o \
crn_ryg_dxt.o \
crn_sparse_bit_array.o \
crn_stb_image.o \
crn_strutils.o \
crn_symbol_codec.o \
crn_texture_file_types.o \
crn_threaded_resampler.o \
crn_threading_pthreads.o \
crn_timer.o \
crn_utils.o \
crn_value.o \
crn_vector.o \
crn_zeng.o \
crn_texture_comp.o \
crn_texture_conversion.o \
crn_dds_comp.o \
crn_lzma_codec.o \
crn_ktx_texture.o \
crn_etc.o \
crn_rg_etc1.o \
crn_miniz.o \
crn_jpge.o \
crn_jpgd.o \
lzma_7zBuf2.o \
lzma_7zBuf.o \
lzma_7zCrc.o \
lzma_7zFile.o \
lzma_7zStream.o \
lzma_Alloc.o \
lzma_Bcj2.o \
lzma_Bra86.o \
lzma_Bra.o \
lzma_BraIA64.o \
lzma_LzFind.o \
lzma_LzmaDec.o \
lzma_LzmaEnc.o \
lzma_LzmaLib.o
all: crunch
%.o: %.cpp
g++ $< -o $@ -c $(COMPILE_OPTIONS)
crunch.o: ../crunch/crunch.cpp
g++ $< -o $@ -c -I../inc -I../crnlib $(COMPILE_OPTIONS)
corpus_gen.o: ../crunch/corpus_gen.cpp
g++ $< -o $@ -c -I../inc -I../crnlib $(COMPILE_OPTIONS)
corpus_test.o: ../crunch/corpus_test.cpp
g++ $< -o $@ -c -I../inc -I../crnlib $(COMPILE_OPTIONS)
crunch: $(OBJECTS) crunch.o corpus_gen.o corpus_test.o
g++ $(OBJECTS) crunch.o corpus_gen.o corpus_test.o -o crunch $(LINKER_OPTIONS)
+313 -13
View File
@@ -16,6 +16,17 @@ namespace crnlib
}; };
}; };
template<> struct color_quad_component_traits<int8>
{
enum
{
cSigned = true,
cFloat = false,
cMin = cINT8_MIN,
cMax = cINT8_MAX
};
};
template<> struct color_quad_component_traits<int16> template<> struct color_quad_component_traits<int16>
{ {
enum enum
@@ -86,21 +97,22 @@ namespace crnlib
class color_quad : public helpers::rel_ops<color_quad<component_type, parameter_type> > class color_quad : public helpers::rel_ops<color_quad<component_type, parameter_type> >
{ {
template<typename T> template<typename T>
static inline T clamp(T v) static inline parameter_type clamp(T v)
{ {
parameter_type result = static_cast<parameter_type>(v);
if (!component_traits::cFloat) if (!component_traits::cFloat)
{ {
if (v < component_traits::cMin) if (v < component_traits::cMin)
v = component_traits::cMin; result = static_cast<parameter_type>(component_traits::cMin);
else if (v > component_traits::cMax) else if (v > component_traits::cMax)
v = component_traits::cMax; result = static_cast<parameter_type>(component_traits::cMax);
} }
return v; return result;
} }
#ifdef _MSC_VER #ifdef _MSC_VER
template<> template<>
static inline int clamp(int v) static inline parameter_type clamp(int v)
{ {
if (!component_traits::cFloat) if (!component_traits::cFloat)
{ {
@@ -117,7 +129,7 @@ namespace crnlib
v = component_traits::cMax; v = component_traits::cMax;
} }
} }
return v; return static_cast<parameter_type>(v);
} }
#endif #endif
@@ -179,7 +191,7 @@ namespace crnlib
template<typename other_component_type, typename other_parameter_type> template<typename other_component_type, typename other_parameter_type>
inline color_quad(const color_quad<other_component_type, other_parameter_type>& other) : inline color_quad(const color_quad<other_component_type, other_parameter_type>& other) :
r(clamp(other.r)), g(clamp(other.g)), b(clamp(other.b)), a(clamp(other.a)) r(static_cast<component_type>(clamp(other.r))), g(static_cast<component_type>(clamp(other.g))), b(static_cast<component_type>(clamp(other.b))), a(static_cast<component_type>(clamp(other.a)))
{ {
} }
@@ -200,13 +212,21 @@ namespace crnlib
return *this; return *this;
} }
inline color_quad& set_rgb(const color_quad& other)
{
r = other.r;
g = other.g;
b = other.b;
return *this;
}
template<typename other_component_type, typename other_parameter_type> template<typename other_component_type, typename other_parameter_type>
inline color_quad& operator=(const color_quad<other_component_type, other_parameter_type>& other) inline color_quad& operator=(const color_quad<other_component_type, other_parameter_type>& other)
{ {
r = clamp(other.r); r = static_cast<component_type>(clamp(other.r));
g = clamp(other.g); g = static_cast<component_type>(clamp(other.g));
b = clamp(other.b); b = static_cast<component_type>(clamp(other.b));
a = clamp(other.a); a = static_cast<component_type>(clamp(other.a));
return *this; return *this;
} }
@@ -519,6 +539,7 @@ namespace crnlib
}; };
typedef color_quad<uint8, int> color_quad_u8; typedef color_quad<uint8, int> color_quad_u8;
typedef color_quad<int8, int> color_quad_i8;
typedef color_quad<int16, int> color_quad_i16; typedef color_quad<int16, int> color_quad_i16;
typedef color_quad<uint16, int> color_quad_u16; typedef color_quad<uint16, int> color_quad_u16;
typedef color_quad<int32, int> color_quad_i32; typedef color_quad<int32, int> color_quad_i32;
@@ -633,7 +654,7 @@ namespace crnlib
const int YR = 19595, YG = 38470, YB = 7471, CB_R = -11059, CB_G = -21709, CB_B = 32768, CR_R = 32768, CR_G = -27439, CR_B = -5329; const int YR = 19595, YG = 38470, YB = 7471, CB_R = -11059, CB_G = -21709, CB_B = 32768, CR_R = 32768, CR_G = -27439, CR_B = -5329;
// YCbCr->RGB constants, scaled by 2^16 // YCbCr->RGB constants, scaled by 2^16
const int R_CR = 91881, B_CB = 116130, G_CR = -46802, G_CB = -22554; const int R_CR = 91881, B_CB = 116130, G_CR = -46802, G_CB = -22554;
inline int RGB_to_Y(const color_quad_u8& rgb) inline int RGB_to_Y(const color_quad_u8& rgb)
{ {
const int r = rgb[0], g = rgb[1], b = rgb[2]; const int r = rgb[0], g = rgb[1], b = rgb[2];
@@ -663,7 +684,7 @@ namespace crnlib
rgb.b = clamp_component(y + ((B_CB * cb + 32768) >> 16)); rgb.b = clamp_component(y + ((B_CB * cb + 32768) >> 16));
rgb.a = 255; rgb.a = 255;
} }
// Float RGB->YCbCr constants // Float RGB->YCbCr constants
const float S = 1.0f/65536.0f; const float S = 1.0f/65536.0f;
const float F_YR = S*YR, F_YG = S*YG, F_YB = S*YB, F_CB_R = S*CB_R, F_CB_G = S*CB_G, F_CB_B = S*CB_B, F_CR_R = S*CR_R, F_CR_G = S*CR_G, F_CR_B = S*CR_B; const float F_YR = S*YR, F_YG = S*YG, F_YB = S*YB, F_CB_R = S*CB_R, F_CB_G = S*CB_G, F_CB_B = S*CB_B, F_CR_R = S*CR_R, F_CR_G = S*CR_G, F_CR_B = S*CR_B;
@@ -690,5 +711,284 @@ namespace crnlib
} // namespace color } // namespace color
// This class purposely trades off speed for extremely flexibility. It can handle any component swizzle, any pixel type from 1-4 components and 1-32 bits/component,
// any pixel size between 1-16 bytes/pixel, any pixel stride, any color_quad data type (signed/unsigned/float 8/16/32 bits/component), and scaled/non-scaled components.
// On the downside, it's freaking slow.
class pixel_packer
{
public:
pixel_packer()
{
clear();
}
pixel_packer(uint num_comps, uint bits_per_comp, int pixel_stride = -1, bool reversed = false)
{
init(num_comps, bits_per_comp, pixel_stride, reversed);
}
pixel_packer(const char* pComp_map, int pixel_stride = -1, int force_comp_size = -1)
{
init(pComp_map, pixel_stride, force_comp_size);
}
void clear()
{
utils::zero_this(this);
}
inline bool is_valid() const { return m_pixel_stride > 0; }
inline uint get_pixel_stride() const { return m_pixel_stride; }
void set_pixel_stride(uint n) { m_pixel_stride = n; }
uint get_num_comps() const { return m_num_comps; }
uint get_comp_size(uint index) const { CRNLIB_ASSERT(index < 4); return m_comp_size[index]; }
uint get_comp_ofs(uint index) const { CRNLIB_ASSERT(index < 4); return m_comp_ofs[index]; }
uint get_comp_max(uint index) const { CRNLIB_ASSERT(index < 4); return m_comp_max[index]; }
bool get_rgb_is_luma() const { return m_rgb_is_luma; }
template<typename color_quad_type>
const void* unpack(const void* p, color_quad_type& color, bool rescale = true) const
{
const uint8* pSrc = static_cast<const uint8*>(p);
for (uint i = 0; i < 4; i++)
{
const uint comp_size = m_comp_size[i];
if (!comp_size)
{
if (color_quad_type::component_traits::cFloat)
color[i] = static_cast< typename color_quad_type::parameter_t >((i == 3) ? 1 : 0);
else
color[i] = static_cast< typename color_quad_type::parameter_t >((i == 3) ? color_quad_type::component_traits::cMax : 0);
continue;
}
uint n = 0, dst_bit_ofs = 0;
uint src_bit_ofs = m_comp_ofs[i];
while (dst_bit_ofs < comp_size)
{
const uint byte_bit_ofs = src_bit_ofs & 7;
n |= ((pSrc[src_bit_ofs >> 3] >> byte_bit_ofs) << dst_bit_ofs);
const uint bits_read = 8 - byte_bit_ofs;
src_bit_ofs += bits_read;
dst_bit_ofs += bits_read;
}
const uint32 mx = m_comp_max[i];
n &= mx;
const uint32 h = static_cast<uint32>(color_quad_type::component_traits::cMax);
if (color_quad_type::component_traits::cFloat)
color.set_component(i, static_cast<typename color_quad_type::parameter_t>(n));
else if (rescale)
color.set_component(i, static_cast<typename color_quad_type::parameter_t>( (static_cast<uint64>(n) * h + (mx >> 1U)) / mx ) );
else if (color_quad_type::component_traits::cSigned)
color.set_component(i, static_cast<typename color_quad_type::parameter_t>(math::minimum<uint32>(n, h)));
else
color.set_component(i, static_cast<typename color_quad_type::parameter_t>(n));
}
if (m_rgb_is_luma)
{
color[0] = color[1];
color[2] = color[1];
}
return pSrc + m_pixel_stride;
}
template<typename color_quad_type>
void* pack(const color_quad_type& color, void* p, bool rescale = true) const
{
uint8* pDst = static_cast<uint8*>(p);
for (uint i = 0; i < 4; i++)
{
const uint comp_size = m_comp_size[i];
if (!comp_size)
continue;
uint32 mx = m_comp_max[i];
uint32 n;
if (color_quad_type::component_traits::cFloat)
{
typename color_quad_type::parameter_t t = color[i];
if (t < 0.0f)
n = 0;
else if (t > static_cast<typename color_quad_type::parameter_t>(mx))
n = mx;
else
n = math::minimum<uint32>(static_cast<uint32>(floor(t + .5f)), mx);
}
else if (rescale)
{
if (color_quad_type::component_traits::cSigned)
n = math::maximum<int>(static_cast<int>(color[i]), 0);
else
n = static_cast<uint32>(color[i]);
const uint32 h = static_cast<uint32>(color_quad_type::component_traits::cMax);
n = static_cast<uint32>((static_cast<uint64>(n) * mx + (h >> 1)) / h);
}
else
{
if (color_quad_type::component_traits::cSigned)
n = math::minimum<uint32>(static_cast<uint32>(math::maximum<int>(static_cast<int>(color[i]), 0)), mx);
else
n = math::minimum<uint32>(static_cast<uint32>(color[i]), mx);
}
uint src_bit_ofs = 0;
uint dst_bit_ofs = m_comp_ofs[i];
while (src_bit_ofs < comp_size)
{
const uint cur_byte_bit_ofs = (dst_bit_ofs & 7);
const uint cur_byte_bits = 8 - cur_byte_bit_ofs;
uint byte_val = pDst[dst_bit_ofs >> 3];
uint bit_mask = (mx << cur_byte_bit_ofs) & 0xFF;
byte_val &= ~bit_mask;
byte_val |= (n << cur_byte_bit_ofs);
pDst[dst_bit_ofs >> 3] = static_cast<uint8>(byte_val);
mx >>= cur_byte_bits;
n >>= cur_byte_bits;
dst_bit_ofs += cur_byte_bits;
src_bit_ofs += cur_byte_bits;
}
}
return pDst + m_pixel_stride;
}
bool init(uint num_comps, uint bits_per_comp, int pixel_stride = -1, bool reversed = false)
{
clear();
if ((num_comps < 1) || (num_comps > 4) || (bits_per_comp < 1) || (bits_per_comp > 32))
{
CRNLIB_ASSERT(0);
return false;
}
for (uint i = 0; i < num_comps; i++)
{
m_comp_size[i] = bits_per_comp;
m_comp_ofs[i] = i * bits_per_comp;
if (reversed)
m_comp_ofs[i] = ((num_comps - 1) * bits_per_comp) - m_comp_ofs[i];
}
for (uint i = 0; i < 4; i++)
m_comp_max[i] = static_cast<uint32>((1ULL << m_comp_size[i]) - 1ULL);
m_pixel_stride = (pixel_stride >= 0) ? pixel_stride : (num_comps * bits_per_comp + 7) / 8;
return true;
}
// Format examples:
// R16G16B16
// B5G6R5
// B5G5R5x1
// Y8A8
// A8R8G8B8
// First component is at LSB in memory. Assumes unsigned integer components, 1-32bits each.
bool init(const char* pComp_map, int pixel_stride = -1, int force_comp_size = -1)
{
clear();
uint cur_bit_ofs = 0;
while (*pComp_map)
{
char c = *pComp_map++;
int comp_index = -1;
if (c == 'R')
comp_index = 0;
else if (c == 'G')
comp_index = 1;
else if (c == 'B')
comp_index = 2;
else if (c == 'A')
comp_index = 3;
else if (c == 'Y')
comp_index = 4;
else if (c != 'x')
return false;
uint comp_size = 0;
uint n = *pComp_map;
if ((n >= '0') && (n <= '9'))
{
comp_size = n - '0';
pComp_map++;
n = *pComp_map;
if ((n >= '0') && (n <= '9'))
{
comp_size = (comp_size * 10) + (n - '0');
pComp_map++;
}
}
if (force_comp_size != -1)
comp_size = force_comp_size;
if ((!comp_size) || (comp_size > 32))
return false;
if (comp_index == 4)
{
if (m_comp_size[0] || m_comp_size[1] || m_comp_size[2])
return false;
//m_comp_ofs[0] = m_comp_ofs[1] = m_comp_ofs[2] = cur_bit_ofs;
//m_comp_size[0] = m_comp_size[1] = m_comp_size[2] = comp_size;
m_comp_ofs[1] = cur_bit_ofs;
m_comp_size[1] = comp_size;
m_rgb_is_luma = true;
m_num_comps++;
}
else if (comp_index >= 0)
{
if (m_comp_size[comp_index])
return false;
m_comp_ofs[comp_index] = cur_bit_ofs;
m_comp_size[comp_index] = comp_size;
m_num_comps++;
}
cur_bit_ofs += comp_size;
}
for (uint i = 0; i < 4; i++)
m_comp_max[i] = static_cast<uint32>((1ULL << m_comp_size[i]) - 1ULL);
if (pixel_stride >= 0)
m_pixel_stride = pixel_stride;
else
m_pixel_stride = (cur_bit_ofs + 7) / 8;
return true;
}
private:
uint m_pixel_stride;
uint m_num_comps;
uint m_comp_size[4];
uint m_comp_ofs[4];
uint m_comp_max[4];
bool m_rgb_is_luma;
};
} // namespace crnlib } // namespace crnlib
+2 -2
View File
@@ -47,7 +47,7 @@ namespace crnlib
if (INVALID_HANDLE_VALUE != cons) if (INVALID_HANDLE_VALUE != cons)
SetConsoleTextAttribute(cons, (WORD)attr); SetConsoleTextAttribute(cons, (WORD)attr);
if (console::get_prefixes()) if ((console::get_prefixes()) && (console::get_at_beginning_of_line()))
{ {
switch (type) switch (type)
{ {
@@ -85,7 +85,7 @@ namespace crnlib
if (console::get_output_disabled()) if (console::get_output_disabled())
return true; return true;
if (console::get_prefixes()) if ((console::get_prefixes()) && (console::get_at_beginning_of_line()))
{ {
switch (type) switch (type)
{ {
+1 -1
View File
@@ -14,7 +14,7 @@
#endif #endif
namespace crnlib namespace crnlib
{ {
void get_command_line(dynamic_string& cmd_line, int argc, char *argv[]) void get_command_line_as_single_string(dynamic_string& cmd_line, int argc, char *argv[])
{ {
argc, argv; argc, argv;
#if CRNLIB_USE_WIN32_API #if CRNLIB_USE_WIN32_API
+3 -1
View File
@@ -6,7 +6,9 @@
namespace crnlib namespace crnlib
{ {
void get_command_line(dynamic_string& cmd_line, int argc, char *argv[]); // Returns the command line passed to the app as a string.
// On systems where this isn't trivial, this function combines together the separate arguments, quoting and adding spaces as needed.
void get_command_line_as_single_string(dynamic_string& cmd_line, int argc, char *argv[]);
class command_line_params class command_line_params
{ {
+13 -8
View File
@@ -285,8 +285,8 @@ namespace crnlib
} }
#if CRNLIB_CREATE_DEBUG_IMAGES #if CRNLIB_CREATE_DEBUG_IMAGES
image_utils::save_to_file(dynamic_string(cVarArg, "color_endpoint_residuals_%u.tga", trial_index).get_ptr(), endpoint_residual_image); image_utils::write_to_file(dynamic_string(cVarArg, "color_endpoint_residuals_%u.tga", trial_index).get_ptr(), endpoint_residual_image);
image_utils::save_to_file(dynamic_string(cVarArg, "color_endpoints_%u.tga", trial_index).get_ptr(), endpoint_image); image_utils::write_to_file(dynamic_string(cVarArg, "color_endpoints_%u.tga", trial_index).get_ptr(), endpoint_image);
#endif #endif
static_huffman_data_model residual_dm[2]; static_huffman_data_model residual_dm[2];
@@ -432,8 +432,8 @@ namespace crnlib
} }
#if CRNLIB_CREATE_DEBUG_IMAGES #if CRNLIB_CREATE_DEBUG_IMAGES
image_utils::save_to_file(dynamic_string(cVarArg, "alpha_endpoint_residuals_%u.tga", trial_index).get_ptr(), endpoint_residual_image); image_utils::write_to_file(dynamic_string(cVarArg, "alpha_endpoint_residuals_%u.tga", trial_index).get_ptr(), endpoint_residual_image);
image_utils::save_to_file(dynamic_string(cVarArg, "alpha_endpoints_%u.tga", trial_index).get_ptr(), endpoint_image); image_utils::write_to_file(dynamic_string(cVarArg, "alpha_endpoints_%u.tga", trial_index).get_ptr(), endpoint_image);
#endif #endif
static_huffman_data_model residual_dm; static_huffman_data_model residual_dm;
@@ -686,8 +686,8 @@ namespace crnlib
} }
#if CRNLIB_CREATE_DEBUG_IMAGES #if CRNLIB_CREATE_DEBUG_IMAGES
image_utils::save_to_file(dynamic_string(cVarArg, "selectors_%u_%u.tga", trial_index, max_selector_value).get_ptr(), selector_image); image_utils::write_to_file(dynamic_string(cVarArg, "selectors_%u_%u.tga", trial_index, max_selector_value).get_ptr(), selector_image);
image_utils::save_to_file(dynamic_string(cVarArg, "selector_residuals_%u_%u.tga", trial_index, max_selector_value).get_ptr(), residual_image); image_utils::write_to_file(dynamic_string(cVarArg, "selector_residuals_%u_%u.tga", trial_index, max_selector_value).get_ptr(), residual_image);
#endif #endif
static_huffman_data_model residual_dm; static_huffman_data_model residual_dm;
@@ -1065,7 +1065,7 @@ namespace crnlib
uint mip_group = 0; uint mip_group = 0;
uint chunk_index = 0; uint chunk_index = 0;
uint mip_group_chunk_index = 0; uint mip_group_chunk_index = 0; (void)mip_group_chunk_index;
for (uint level_index = 0; level_index < m_pParams->m_levels; level_index++) for (uint level_index = 0; level_index < m_pParams->m_levels; level_index++)
{ {
const uint width = math::maximum(1U, m_pParams->m_width >> level_index); const uint width = math::maximum(1U, m_pParams->m_width >> level_index);
@@ -1342,6 +1342,11 @@ namespace crnlib
params.m_perceptual = false; params.m_perceptual = false;
break; break;
} }
case cCRNFmtETC1:
{
console::warning("crn_comp::quantize_chunks: This class does not support ETC1");
return false;
}
default: default:
{ {
return false; return false;
@@ -1366,7 +1371,7 @@ namespace crnlib
image_u8 img; image_u8 img;
dxt_hc::create_debug_image_from_chunks((m_pParams->m_width+7)>>3, (m_pParams->m_height+7)>>3, pixel_chunks, &m_hvq.get_chunk_encoding_vec(), img, true, -1); dxt_hc::create_debug_image_from_chunks((m_pParams->m_width+7)>>3, (m_pParams->m_height+7)>>3, pixel_chunks, &m_hvq.get_chunk_encoding_vec(), img, true, -1);
image_utils::save_to_file("quantized_chunks.tga", img); image_utils::write_to_file("quantized_chunks.tga", img);
} }
#endif #endif
+5 -1
View File
@@ -15,6 +15,7 @@ namespace crnlib
data_stream* console::m_pLog_stream; data_stream* console::m_pLog_stream;
mutex* console::m_pMutex; mutex* console::m_pMutex;
uint console::m_num_messages[cCMTTotal]; uint console::m_num_messages[cCMTTotal];
bool console::m_at_beginning_of_line = true;
const uint cConsoleBufSize = 4096; const uint cConsoleBufSize = 4096;
@@ -70,7 +71,7 @@ namespace crnlib
} }
const char* pPrefix = NULL; const char* pPrefix = NULL;
if (m_prefixes) if ((m_prefixes) && (m_at_beginning_of_line))
{ {
switch (type) switch (type)
{ {
@@ -88,6 +89,9 @@ namespace crnlib
::printf(m_crlf ? "%s\n" : "%s", buf); ::printf(m_crlf ? "%s\n" : "%s", buf);
} }
uint n = strlen(buf);
m_at_beginning_of_line = (m_crlf) || ((n) && (buf[n - 1] == '\n'));
if ((type != cProgressConsoleMessage) && (m_pLog_stream)) if ((type != cProgressConsoleMessage) && (m_pLog_stream))
{ {
// Yes this is bad. // Yes this is bad.
+3
View File
@@ -59,6 +59,7 @@ namespace crnlib
static void disable_prefixes(); static void disable_prefixes();
static void enable_prefixes(); static void enable_prefixes();
static bool get_prefixes() { return m_prefixes; } static bool get_prefixes() { return m_prefixes; }
static bool get_at_beginning_of_line() { return m_at_beginning_of_line; }
static void disable_crlf(); static void disable_crlf();
static void enable_crlf(); static void enable_crlf();
@@ -92,6 +93,8 @@ namespace crnlib
static mutex* m_pMutex; static mutex* m_pMutex;
static uint m_num_messages[cCMTTotal]; static uint m_num_messages[cCMTTotal];
static bool m_at_beginning_of_line;
}; };
#if defined(WIN32) #if defined(WIN32)
+1 -1
View File
@@ -12,7 +12,7 @@
#if defined(WIN32) && !defined(CRNLIB_ANSI_CPLUSPLUS) #if defined(WIN32) && !defined(CRNLIB_ANSI_CPLUSPLUS)
// MSVC or MinGW, x86 or x64, Win32 API's for threading and Win32 Interlocked API's or GCC built-ins for atomic ops. // MSVC or MinGW, x86 or x64, Win32 API's for threading and Win32 Interlocked API's or GCC built-ins for atomic ops.
#ifdef NDEBUG #ifdef NDEBUG
// Ensure checked iterators are disabled. // Ensure checked iterators are disabled. Note: Be sure anything else that links against this lib also #define's this stuff, or remove this crap!
#define _SECURE_SCL 0 #define _SECURE_SCL 0
#define _HAS_ITERATOR_DEBUGGING 0 #define _HAS_ITERATOR_DEBUGGING 0
#endif #endif
+36
View File
@@ -19,6 +19,8 @@ namespace crnlib
data_stream* get_stream() const { return m_pStream; } data_stream* get_stream() const { return m_pStream; }
void set_stream(data_stream* pStream) { m_pStream = pStream; } void set_stream(data_stream* pStream) { m_pStream = pStream; }
const dynamic_string& get_name() const { return m_pStream ? m_pStream->get_name() : g_empty_dynamic_string; }
bool get_error() { return m_pStream ? m_pStream->get_error() : false; } bool get_error() { return m_pStream ? m_pStream->get_error() : false; }
bool get_little_endian() const { return m_little_endian; } bool get_little_endian() const { return m_little_endian; }
@@ -34,6 +36,30 @@ namespace crnlib
return m_pStream->read(pBuf, len) == len; return m_pStream->read(pBuf, len) == len;
} }
// size = size of each element, count = number of elements, returns actual count of elements written
uint write(const void* pBuf, uint size, uint count)
{
uint actual_size = size * count;
if (!actual_size)
return 0;
uint n = m_pStream->write(pBuf, actual_size);
if (n == actual_size)
return count;
return n / size;
}
// size = size of each element, count = number of elements, returns actual count of elements read
uint read(void* pBuf, uint size, uint count)
{
uint actual_size = size * count;
if (!actual_size)
return 0;
uint n = m_pStream->read(pBuf, actual_size);
if (n == actual_size)
return count;
return n / size;
}
bool write_chars(const char* pBuf, uint len) bool write_chars(const char* pBuf, uint len)
{ {
return write(pBuf, len); return write(pBuf, len);
@@ -249,6 +275,16 @@ namespace crnlib
return true; return true;
} }
bool read_entire_file(crnlib::vector<uint8>& buf)
{
return m_pStream->read_array(buf);
}
bool write_entire_file(const crnlib::vector<uint8>& buf)
{
return m_pStream->write_array(buf);
}
// Got this idea from the Molly Rocket forums. // Got this idea from the Molly Rocket forums.
// fmt may contain the characters "1", "2", or "4". // fmt may contain the characters "1", "2", or "4".
+2 -2
View File
@@ -34,7 +34,7 @@ namespace crnlib
} }
} }
bool dds_comp::create_dds_tex(dds_texture &dds_tex) bool dds_comp::create_dds_tex(mipmapped_texture &dds_tex)
{ {
image_u8 images[cCRNMaxFaces][cCRNMaxLevels]; image_u8 images[cCRNMaxFaces][cCRNMaxLevels];
@@ -137,7 +137,7 @@ namespace crnlib
if (!m_pQDXT_state) if (!m_pQDXT_state)
{ {
m_pQDXT_state = crnlib_new<dds_texture::qdxt_state>(m_task_pool); m_pQDXT_state = crnlib_new<mipmapped_texture::qdxt_state>(m_task_pool);
if (params.m_pProgress_func) if (params.m_pProgress_func)
{ {
+5 -5
View File
@@ -2,7 +2,7 @@
// See Copyright Notice and license at the end of inc/crnlib.h // See Copyright Notice and license at the end of inc/crnlib.h
#pragma once #pragma once
#include "crn_comp.h" #include "crn_comp.h"
#include "crn_dds_texture.h" #include "crn_mipmapped_texture.h"
#include "crn_texture_comp.h" #include "crn_texture_comp.h"
namespace crnlib namespace crnlib
@@ -25,8 +25,8 @@ namespace crnlib
virtual crnlib::vector<uint8>& get_comp_data() { return m_comp_data; } virtual crnlib::vector<uint8>& get_comp_data() { return m_comp_data; }
private: private:
dds_texture m_src_tex; mipmapped_texture m_src_tex;
dds_texture m_packed_tex; mipmapped_texture m_packed_tex;
crnlib::vector<uint8> m_comp_data; crnlib::vector<uint8> m_comp_data;
@@ -38,10 +38,10 @@ namespace crnlib
task_pool m_task_pool; task_pool m_task_pool;
qdxt1_params m_q1_params; qdxt1_params m_q1_params;
qdxt5_params m_q5_params; qdxt5_params m_q5_params;
dds_texture::qdxt_state *m_pQDXT_state; mipmapped_texture::qdxt_state *m_pQDXT_state;
void clear(); void clear();
bool create_dds_tex(dds_texture &dds_tex); bool create_dds_tex(mipmapped_texture &dds_tex);
bool convert_to_dxt(const crn_comp_params& params); bool convert_to_dxt(const crn_comp_params& params);
}; };
+2
View File
@@ -31,6 +31,7 @@ namespace crnlib
case cDXT5A: return "DXT5A"; case cDXT5A: return "DXT5A";
case cDXN_XY: return "DXN_XY"; case cDXN_XY: return "DXN_XY";
case cDXN_YX: return "DXN_YX"; case cDXN_YX: return "DXN_YX";
case cETC1: return "ETC1";
default: break; default: break;
} }
CRNLIB_ASSERT(false); CRNLIB_ASSERT(false);
@@ -60,6 +61,7 @@ namespace crnlib
case cDXT1: case cDXT1:
case cDXT1A: case cDXT1A:
case cDXT5A: case cDXT5A:
case cETC1:
return 4; return 4;
case cDXT3: case cDXT3:
case cDXT5: case cDXT5:
+81 -1
View File
@@ -43,7 +43,9 @@ namespace crnlib
cDXT5A, cDXT5A,
cDXN_XY, // inverted relative to standard ATI2, 360's DXN cDXN_XY, // inverted relative to standard ATI2, 360's DXN
cDXN_YX // standard ATI2 cDXN_YX, // standard ATI2,
cETC1 // Ericsson texture compression (color only, 4x4 blocks, 4bpp, 64-bits/block)
}; };
const float cDXT1MaxLinearValue = 3.0f; const float cDXT1MaxLinearValue = 3.0f;
@@ -131,6 +133,32 @@ namespace crnlib
m_selectors[y] |= (val << (x * cDXT1SelectorBits)); m_selectors[y] |= (val << (x * cDXT1SelectorBits));
} }
inline void flip_x(uint w = 4, uint h = 4)
{
for (uint x = 0; x < (w / 2); x++)
{
for (uint y = 0; y < h; y++)
{
const uint c = get_selector(x, y);
set_selector(x, y, get_selector((w - 1) - x, y));
set_selector((w - 1) - x, y, c);
}
}
}
inline void flip_y(uint w = 4, uint h = 4)
{
for (uint y = 0; y < (h / 2); y++)
{
for (uint x = 0; x < w; x++)
{
const uint c = get_selector(x, y);
set_selector(x, y, get_selector(x, (h - 1) - y));
set_selector(x, (h - 1) - y, c);
}
}
}
static uint16 pack_color(const color_quad_u8& color, bool scaled, uint bias = 127U); static uint16 pack_color(const color_quad_u8& color, bool scaled, uint bias = 127U);
static uint16 pack_color(uint r, uint g, uint b, bool scaled, uint bias = 127U); static uint16 pack_color(uint r, uint g, uint b, bool scaled, uint bias = 127U);
@@ -163,6 +191,32 @@ namespace crnlib
void set_alpha(uint x, uint y, uint value, bool scaled); void set_alpha(uint x, uint y, uint value, bool scaled);
uint get_alpha(uint x, uint y, bool scaled) const; uint get_alpha(uint x, uint y, bool scaled) const;
inline void flip_x(uint w = 4, uint h = 4)
{
for (uint x = 0; x < (w / 2); x++)
{
for (uint y = 0; y < h; y++)
{
const uint c = get_alpha(x, y, false);
set_alpha(x, y, get_alpha((w - 1) - x, y, false), false);
set_alpha((w - 1) - x, y, c, false);
}
}
}
inline void flip_y(uint w = 4, uint h = 4)
{
for (uint y = 0; y < (h / 2); y++)
{
for (uint x = 0; x < w; x++)
{
const uint c = get_alpha(x, y, false);
set_alpha(x, y, get_alpha(x, (h - 1) - y, false), false);
set_alpha(x, (h - 1) - y, c, false);
}
}
}
}; };
CRNLIB_DEFINE_BITWISE_COPYABLE(dxt3_block); CRNLIB_DEFINE_BITWISE_COPYABLE(dxt3_block);
@@ -245,6 +299,32 @@ namespace crnlib
m_selectors[byte_index + 1] = static_cast<uint8>(v >> 8); m_selectors[byte_index + 1] = static_cast<uint8>(v >> 8);
} }
inline void flip_x(uint w = 4, uint h = 4)
{
for (uint x = 0; x < (w / 2); x++)
{
for (uint y = 0; y < h; y++)
{
const uint c = get_selector(x, y);
set_selector(x, y, get_selector((w - 1) - x, y));
set_selector((w - 1) - x, y, c);
}
}
}
inline void flip_y(uint w = 4, uint h = 4)
{
for (uint y = 0; y < (h / 2); y++)
{
for (uint x = 0; x < w; x++)
{
const uint c = get_selector(x, y);
set_selector(x, y, get_selector(x, (h - 1) - y));
set_selector(x, (h - 1) - y, c);
}
}
}
enum { cMaxSelectorValues = 8 }; enum { cMaxSelectorValues = 8 };
// Results written to alpha channel. // Results written to alpha channel.
+91 -58
View File
@@ -1,5 +1,13 @@
// File: crn_dxt1.cpp // File: crn_dxt1.cpp
// See Copyright Notice and license at the end of inc/crnlib.h // See Copyright Notice and license at the end of inc/crnlib.h
//
// Notes:
// This class is not optimized for performance on small blocks, unlike typical DXT1 compressors. It's optimized for scalability and quality:
// - Very high quality in terms of avg. RMSE or Luma RMSE. Goal is to always match or beat every other known offline DXTc compressor: ATI_Compress, squish, NVidia texture tools, nvdxt.exe, etc.
// - Reasonable scalability and stability with hundreds to many thousands of input colors (including inputs with many thousands of equal/nearly equal colors).
// - Any quality optimization which results in even a tiny improvement is worth it -- as long as it's either a constant or linear slowdown.
// Tiny quality improvements can be extremely valuable in large clusters.
// - Quality should scale well vs. CPU time cost, i.e. the more time you spend the higher the quality.
#include "crn_core.h" #include "crn_core.h"
#include "crn_dxt1.h" #include "crn_dxt1.h"
#include "crn_ryg_dxt.hpp" #include "crn_ryg_dxt.hpp"
@@ -9,6 +17,22 @@
namespace crnlib namespace crnlib
{ {
//-----------------------------------------------------------------------------------------------------------------------------------------
static const int16 g_fast_probe_table[] = { 0, 1, 2, 3 };
static const uint cFastProbeTableSize = sizeof(g_fast_probe_table) / sizeof(g_fast_probe_table[0]);
static const int16 g_normal_probe_table[] = { 0, 1, 3, 5, 7 };
static const uint cNormalProbeTableSize = sizeof(g_normal_probe_table) / sizeof(g_normal_probe_table[0]);
static const int16 g_better_probe_table[] = { 0, 1, 2, 3, 5, 9, 15, 19, 27, 43 };
static const uint cBetterProbeTableSize = sizeof(g_better_probe_table) / sizeof(g_better_probe_table[0]);
static const int16 g_uber_probe_table[] = { 0, 1, 2, 3, 5, 7, 9, 10, 13, 15, 19, 27, 43, 59, 91 };
static const uint cUberProbeTableSize = sizeof(g_uber_probe_table) / sizeof(g_uber_probe_table[0]);
//-----------------------------------------------------------------------------------------------------------------------------------------
dxt1_endpoint_optimizer::dxt1_endpoint_optimizer() : dxt1_endpoint_optimizer::dxt1_endpoint_optimizer() :
m_pParams(NULL), m_pParams(NULL),
m_pResults(NULL), m_pResults(NULL),
@@ -75,6 +99,7 @@ namespace crnlib
return true; return true;
} }
// All selectors are equal. Try compressing as if it was solid, using the block's average color, using ryg's optimal single color compression tables.
bool dxt1_endpoint_optimizer::try_average_block_as_solid() bool dxt1_endpoint_optimizer::try_average_block_as_solid()
{ {
uint64 tot_r = 0; uint64 tot_r = 0;
@@ -110,6 +135,7 @@ namespace crnlib
if (m_pParams->m_quality == cCRNDXTQualityUber) if (m_pParams->m_quality == cCRNDXTQualityUber)
{ {
// Try compressing as all-solid using the other (non-average) colors in the block in uber.
for (uint i = 0; i < m_unique_colors.size(); i++) for (uint i = 0; i < m_unique_colors.size(); i++)
{ {
uint r = m_unique_colors[i].m_color[0]; uint r = m_unique_colors[i].m_color[0];
@@ -134,6 +160,7 @@ namespace crnlib
return improved; return improved;
} }
// Block is solid, trying using ryg's optimal single color tables.
bool dxt1_endpoint_optimizer::handle_solid_block() bool dxt1_endpoint_optimizer::handle_solid_block()
{ {
int r = m_unique_colors[0].m_color.r; int r = m_unique_colors[0].m_color.r;
@@ -195,6 +222,7 @@ namespace crnlib
} }
} }
// Compute PCA (principle axis, i.e. direction of largest variance) of input vectors.
void dxt1_endpoint_optimizer::compute_pca(vec3F& axis, const vec3F_array& norm_colors, const vec3F& def) void dxt1_endpoint_optimizer::compute_pca(vec3F& axis, const vec3F_array& norm_colors, const vec3F& def)
{ {
#if 0 #if 0
@@ -202,6 +230,7 @@ namespace crnlib
CRNLIB_ASSERT(m_unique_colors.size() == norm_colors.size()); CRNLIB_ASSERT(m_unique_colors.size() == norm_colors.size());
// Incremental PCA
bool first = true; bool first = true;
for (uint i = 0; i < norm_colors.size(); i++) for (uint i = 0; i < norm_colors.size(); i++)
{ {
@@ -272,6 +301,7 @@ namespace crnlib
//vfr = hi[0] - lo[0]; //vfr = hi[0] - lo[0];
//vfg = hi[1] - lo[1]; //vfg = hi[1] - lo[1];
//vfb = hi[2] - lo[2]; //vfb = hi[2] - lo[2];
// This is more stable.
vfr = .9f; vfr = .9f;
vfg = 1.0f; vfg = 1.0f;
vfb = .7f; vfb = .7f;
@@ -325,6 +355,7 @@ namespace crnlib
static const uint8 g_invTableAlpha[4] = { 1, 0, 2, 3 }; static const uint8 g_invTableAlpha[4] = { 1, 0, 2, 3 };
static const uint8 g_invTableColor[4] = { 1, 0, 3, 2 }; static const uint8 g_invTableColor[4] = { 1, 0, 3, 2 };
// Computes a valid (encodable) DXT1 solution (low/high colors, swizzled selectors) from input.
void dxt1_endpoint_optimizer::return_solution(results& res, const potential_solution& solution) void dxt1_endpoint_optimizer::return_solution(results& res, const potential_solution& solution)
{ {
bool invert_selectors; bool invert_selectors;
@@ -433,6 +464,7 @@ namespace crnlib
return vec3F(c.r, c.g, c.b); return vec3F(c.r, c.g, c.b);
} }
// Per-component 1D endpoint optimization.
void dxt1_endpoint_optimizer::optimize_endpoint_comps() void dxt1_endpoint_optimizer::optimize_endpoint_comps()
{ {
if ((m_best_solution.m_alpha_block) || (!m_best_solution.m_error)) if ((m_best_solution.m_alpha_block) || (!m_best_solution.m_error))
@@ -583,6 +615,7 @@ namespace crnlib
} // comp_index } // comp_index
} }
// Voxel adjacency delta coordinations.
static const struct adjacent_coords static const struct adjacent_coords
{ {
int8 x, y, z; int8 x, y, z;
@@ -618,6 +651,7 @@ namespace crnlib
{1, 1, 1} {1, 1, 1}
}; };
// Attempt to refine current solution's endpoints given the current selectors using least squares.
bool dxt1_endpoint_optimizer::refine_solution(int refinement_level) bool dxt1_endpoint_optimizer::refine_solution(int refinement_level)
{ {
CRNLIB_ASSERT(m_best_solution.m_valid); CRNLIB_ASSERT(m_best_solution.m_valid);
@@ -694,11 +728,15 @@ namespace crnlib
} }
else if (refinement_level == 1) else if (refinement_level == 1)
{ {
// Try exploring the local lattice neighbors of the least squares optimized result.
color_quad_u8 e[2]; color_quad_u8 e[2];
e[0].clear();
e[0][0] = (uint8)math::clamp<int>(static_cast<int>((At1_r*yy - At2_r*xy)*frb+0.5f),0,31); e[0][0] = (uint8)math::clamp<int>(static_cast<int>((At1_r*yy - At2_r*xy)*frb+0.5f),0,31);
e[0][1] = (uint8)math::clamp<int>(static_cast<int>((At1_g*yy - At2_g*xy)*fg +0.5f),0,63); e[0][1] = (uint8)math::clamp<int>(static_cast<int>((At1_g*yy - At2_g*xy)*fg +0.5f),0,63);
e[0][2] = (uint8)math::clamp<int>(static_cast<int>((At1_b*yy - At2_b*xy)*frb+0.5f),0,31); e[0][2] = (uint8)math::clamp<int>(static_cast<int>((At1_b*yy - At2_b*xy)*frb+0.5f),0,31);
e[1].clear();
e[1][0] = (uint8)math::clamp<int>(static_cast<int>((At2_r*xx - At1_r*xy)*frb+0.5f),0,31); e[1][0] = (uint8)math::clamp<int>(static_cast<int>((At2_r*xx - At1_r*xy)*frb+0.5f),0,31);
e[1][1] = (uint8)math::clamp<int>(static_cast<int>((At2_g*xx - At1_g*xy)*fg +0.5f),0,63); e[1][1] = (uint8)math::clamp<int>(static_cast<int>((At2_g*xx - At1_g*xy)*fg +0.5f),0,63);
e[1][2] = (uint8)math::clamp<int>(static_cast<int>((At2_b*xx - At1_b*xy)*frb+0.5f),0,31); e[1][2] = (uint8)math::clamp<int>(static_cast<int>((At2_b*xx - At1_b*xy)*frb+0.5f),0,31);
@@ -737,11 +775,14 @@ namespace crnlib
} }
else else
{ {
// Try even harder to explore the local lattice neighbors of the least squares optimized result.
color_quad_u8 e[2]; color_quad_u8 e[2];
e[0].clear();
e[0][0] = (uint8)math::clamp<int>(static_cast<int>((At1_r*yy - At2_r*xy)*frb+0.5f),0,31); e[0][0] = (uint8)math::clamp<int>(static_cast<int>((At1_r*yy - At2_r*xy)*frb+0.5f),0,31);
e[0][1] = (uint8)math::clamp<int>(static_cast<int>((At1_g*yy - At2_g*xy)*fg +0.5f),0,63); e[0][1] = (uint8)math::clamp<int>(static_cast<int>((At1_g*yy - At2_g*xy)*fg +0.5f),0,63);
e[0][2] = (uint8)math::clamp<int>(static_cast<int>((At1_b*yy - At2_b*xy)*frb+0.5f),0,31); e[0][2] = (uint8)math::clamp<int>(static_cast<int>((At1_b*yy - At2_b*xy)*frb+0.5f),0,31);
e[1].clear();
e[1][0] = (uint8)math::clamp<int>(static_cast<int>((At2_r*xx - At1_r*xy)*frb+0.5f),0,31); e[1][0] = (uint8)math::clamp<int>(static_cast<int>((At2_r*xx - At1_r*xy)*frb+0.5f),0,31);
e[1][1] = (uint8)math::clamp<int>(static_cast<int>((At2_g*xx - At1_g*xy)*fg +0.5f),0,63); e[1][1] = (uint8)math::clamp<int>(static_cast<int>((At2_g*xx - At1_g*xy)*fg +0.5f),0,63);
e[1][2] = (uint8)math::clamp<int>(static_cast<int>((At2_b*xx - At1_b*xy)*frb+0.5f),0,31); e[1][2] = (uint8)math::clamp<int>(static_cast<int>((At2_b*xx - At1_b*xy)*frb+0.5f),0,31);
@@ -790,63 +831,7 @@ namespace crnlib
//----------------------------------------------------------------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------------------------------------------------------------
static int16 g_fast_probe_table[] = // Primary endpoint optimization entrypoint.
{
0,
1,
2,
3
};
const uint cFastProbeTableSize = sizeof(g_fast_probe_table) / sizeof(g_fast_probe_table[0]);
static int16 g_normal_probe_table[] =
{
0,
1,
3,
5,
7
};
const uint cNormalProbeTableSize = sizeof(g_normal_probe_table) / sizeof(g_normal_probe_table[0]);
static int16 g_better_probe_table[] =
{
0,
1,
2,
3,
5,
9,
15,
19,
27,
43
};
const uint cBetterProbeTableSize = sizeof(g_better_probe_table) / sizeof(g_better_probe_table[0]);
static int16 g_uber_probe_table[] =
{
0,
1,
2,
3,
5,
7,
9,
10,
13,
15,
19,
27,
43,
59,
91
};
const uint cUberProbeTableSize = sizeof(g_uber_probe_table) / sizeof(g_uber_probe_table[0]);
bool dxt1_endpoint_optimizer::optimize_endpoints(vec3F& low_color, vec3F& high_color) bool dxt1_endpoint_optimizer::optimize_endpoints(vec3F& low_color, vec3F& high_color)
{ {
vec3F orig_low_color(low_color); vec3F orig_low_color(low_color);
@@ -855,10 +840,11 @@ namespace crnlib
m_trial_solution.clear(); m_trial_solution.clear();
uint num_passes; uint num_passes;
int16* pProbe_table = g_uber_probe_table; const int16* pProbe_table = g_uber_probe_table;
uint probe_range; uint probe_range;
float dist_per_trial = .015625f; float dist_per_trial = .015625f;
// How many probes, and the distance between each probe depends on the quality level.
switch (m_pParams->m_quality) switch (m_pParams->m_quality)
{ {
case cCRNDXTQualitySuperFast: case cCRNDXTQualitySuperFast:
@@ -895,6 +881,7 @@ namespace crnlib
if (m_pParams->m_endpoint_caching) if (m_pParams->m_endpoint_caching)
{ {
// Try the previous X winning endpoints. This may not give us optimal results, but it may increase the probability of early outs while evaluating potential solutions.
const uint num_prev_results = math::minimum<uint>(cMaxPrevResults, m_num_prev_results); const uint num_prev_results = math::minimum<uint>(cMaxPrevResults, m_num_prev_results);
for (uint i = 0; i < num_prev_results; i++) for (uint i = 0; i < num_prev_results; i++)
{ {
@@ -909,6 +896,7 @@ namespace crnlib
if (!m_best_solution.m_error) if (!m_best_solution.m_error)
{ {
// Got lucky - one of the previous endpoints is optimal.
return_solution(*m_pResults, m_best_solution); return_solution(*m_pResults, m_best_solution);
return true; return true;
} }
@@ -949,6 +937,12 @@ namespace crnlib
for (uint pass = 0; pass < num_passes; pass++) for (uint pass = 0; pass < num_passes; pass++)
{ {
// Now separately sweep or probe the low and high colors along the principle axis, both positively and negatively.
// This results in two arrays of candidate low/high endpoints. Every unique combination of candidate endpoints is tried as a potential solution.
// In higher quality modes, the various nearby lattice neighbors of each candidate endpoint are also explored, which allows the current solution to "wobble" or "migrate"
// to areas with lower error.
// This entire process can be repeated up to X times (depending on the quality level) until a local minimum is established.
// This method is very stable and scalable. It could be implemented more elegantly, but I'm now very cautious of touching this code.
if (pass) if (pass)
{ {
low_color = unpack_to_vec3F_raw(m_best_solution.m_coords.m_low_color); low_color = unpack_to_vec3F_raw(m_best_solution.m_coords.m_low_color);
@@ -959,6 +953,7 @@ namespace crnlib
if (!prev_best_error) if (!prev_best_error)
break; break;
// Sweep low endpoint along principle axis, record positions
int prev_packed_color[2] = { -1, -1 }; int prev_packed_color[2] = { -1, -1 };
uint num_low_trials = 0; uint num_low_trials = 0;
vec3F initial_probe_low_color(low_color + vec3F(.5f)); vec3F initial_probe_low_color(low_color + vec3F(.5f));
@@ -987,6 +982,7 @@ namespace crnlib
prev_packed_color[0] = -1; prev_packed_color[0] = -1;
prev_packed_color[1] = -1; prev_packed_color[1] = -1;
// Sweep high endpoint along principle axis, record positions
uint num_high_trials = 0; uint num_high_trials = 0;
vec3F initial_probe_high_color(high_color + vec3F(.5f)); vec3F initial_probe_high_color(high_color + vec3F(.5f));
for (uint i = 0; i < probe_range; i++) for (uint i = 0; i < probe_range; i++)
@@ -1011,6 +1007,7 @@ namespace crnlib
} }
} }
// Now try all unique combinations.
for (uint i = 0; i < num_low_trials; i++) for (uint i = 0; i < num_low_trials; i++)
{ {
for (uint j = 0; j < num_high_trials; j++) for (uint j = 0; j < num_high_trials; j++)
@@ -1028,6 +1025,7 @@ namespace crnlib
if (m_pParams->m_quality >= cCRNDXTQualityNormal) if (m_pParams->m_quality >= cCRNDXTQualityNormal)
{ {
// Generate new candidates by exploring the low color's direct lattice neighbors
color_quad_u8 lc(dxt1_block::unpack_color(m_best_solution.m_coords.m_low_color, false)); color_quad_u8 lc(dxt1_block::unpack_color(m_best_solution.m_coords.m_low_color, false));
for (int i = 0; i < 26; i++) for (int i = 0; i < 26; i++)
@@ -1051,6 +1049,7 @@ namespace crnlib
if (m_pParams->m_quality == cCRNDXTQualityUber) if (m_pParams->m_quality == cCRNDXTQualityUber)
{ {
// Generate new candidates by exploring the low color's direct lattice neighbors - this time, explore much further separately on each axis.
lc = dxt1_block::unpack_color(m_best_solution.m_coords.m_low_color, false); lc = dxt1_block::unpack_color(m_best_solution.m_coords.m_low_color, false);
for (int a = 0; a < 3; a++) for (int a = 0; a < 3; a++)
@@ -1075,6 +1074,7 @@ namespace crnlib
} }
} }
// Generate new candidates by exploring the high color's direct lattice neighbors
color_quad_u8 hc(dxt1_block::unpack_color(m_best_solution.m_coords.m_high_color, false)); color_quad_u8 hc(dxt1_block::unpack_color(m_best_solution.m_coords.m_high_color, false));
for (int i = 0; i < 26; i++) for (int i = 0; i < 26; i++)
@@ -1098,6 +1098,7 @@ namespace crnlib
if (m_pParams->m_quality == cCRNDXTQualityUber) if (m_pParams->m_quality == cCRNDXTQualityUber)
{ {
// Generate new candidates by exploring the high color's direct lattice neighbors - this time, explore much further separately on each axis.
hc = dxt1_block::unpack_color(m_best_solution.m_coords.m_high_color, false); hc = dxt1_block::unpack_color(m_best_solution.m_coords.m_high_color, false);
for (int a = 0; a < 3; a++) for (int a = 0; a < 3; a++)
@@ -1127,7 +1128,10 @@ namespace crnlib
break; break;
if (m_pParams->m_quality >= cCRNDXTQualityUber) if (m_pParams->m_quality >= cCRNDXTQualityUber)
{
// Attempt to refine current solution's endpoints given the current selectors using least squares.
refine_solution(1); refine_solution(1);
}
} }
if (m_pParams->m_quality >= cCRNDXTQualityNormal) if (m_pParams->m_quality >= cCRNDXTQualityNormal)
@@ -1136,16 +1140,26 @@ namespace crnlib
{ {
bool choose_solid_block = false; bool choose_solid_block = false;
if (m_best_solution.are_selectors_all_equal()) if (m_best_solution.are_selectors_all_equal())
{
// All selectors equal - try various solid-block optimizations
choose_solid_block = try_average_block_as_solid(); choose_solid_block = try_average_block_as_solid();
}
if ((!choose_solid_block) && (m_pParams->m_quality == cCRNDXTQualityUber)) if ((!choose_solid_block) && (m_pParams->m_quality == cCRNDXTQualityUber))
{
// Per-component 1D endpoint optimization.
optimize_endpoint_comps(); optimize_endpoint_comps();
}
} }
if (m_pParams->m_quality == cCRNDXTQualityUber) if (m_pParams->m_quality == cCRNDXTQualityUber)
{ {
if (m_best_solution.m_error) if (m_best_solution.m_error)
{
// The pixels may have already been DXTc compressed by another compressor.
// It's usually possible to recover the endpoints used to previously pack the block.
try_combinatorial_encoding(); try_combinatorial_encoding();
}
} }
} }
@@ -1153,6 +1167,7 @@ namespace crnlib
if (m_pParams->m_endpoint_caching) if (m_pParams->m_endpoint_caching)
{ {
// Remember result for later reruse.
m_prev_results[m_num_prev_results & (cMaxPrevResults - 1)] = m_best_solution.m_coords; m_prev_results[m_num_prev_results & (cMaxPrevResults - 1)] = m_best_solution.m_coords;
m_num_prev_results++; m_num_prev_results++;
} }
@@ -1173,6 +1188,8 @@ namespace crnlib
if (m_perceptual) if (m_perceptual)
{ {
// Compute RGB weighting for use in perceptual mode.
// The more saturated the block, the more the weights deviate from (1,1,1).
float ave_redness = 0; float ave_redness = 0;
float ave_blueness = 0; float ave_blueness = 0;
float ave_l = 0; float ave_l = 0;
@@ -1224,6 +1241,8 @@ namespace crnlib
im.transpose_in_place(); im.transpose_in_place();
m_principle_axis = m_principle_axis * im; m_principle_axis = m_principle_axis * im;
#else #else
// Purposely scale the components of the principle axis by the perceptual weighting.
// There's probably a cleaner way to go about this, but it works (more competitive in perceptual mode against nvdxt.exe or ATI_Compress).
m_principle_axis[0] /= perceptual_weights[0]; m_principle_axis[0] /= perceptual_weights[0];
m_principle_axis[1] /= perceptual_weights[1]; m_principle_axis[1] /= perceptual_weights[1];
m_principle_axis[2] /= perceptual_weights[2]; m_principle_axis[2] /= perceptual_weights[2];
@@ -1232,6 +1251,7 @@ namespace crnlib
if (num_passes > 1) if (num_passes > 1)
{ {
// Check for obviously wild principle axes and try to compensate by backing off the component weightings.
if (fabs(m_principle_axis[0]) >= .795f) if (fabs(m_principle_axis[0]) >= .795f)
perceptual_weights.set(.424f, .6f, .072f); perceptual_weights.set(.424f, .6f, .072f);
else if (fabs(m_principle_axis[2]) >= .795f) else if (fabs(m_principle_axis[2]) >= .795f)
@@ -1241,6 +1261,7 @@ namespace crnlib
} }
} }
// Find bounds of projection onto (potentially skewed) principle axis.
float l = 1e+9; float l = 1e+9;
float h = -1e+9; float h = -1e+9;
@@ -1256,6 +1277,7 @@ namespace crnlib
if (!low_color.is_within_bounds(0.0f, 1.0f)) if (!low_color.is_within_bounds(0.0f, 1.0f))
{ {
// Low color is outside the lattice, so bring it back in by casting a ray.
vec3F coord; vec3F coord;
float t; float t;
aabb3F bounds(vec3F(0.0f), vec3F(1.0f)); aabb3F bounds(vec3F(0.0f), vec3F(1.0f));
@@ -1266,6 +1288,7 @@ namespace crnlib
if (!high_color.is_within_bounds(0.0f, 1.0f)) if (!high_color.is_within_bounds(0.0f, 1.0f))
{ {
// High color is outside the lattice, so bring it back in by casting a ray.
vec3F coord; vec3F coord;
float t; float t;
aabb3F bounds(vec3F(0.0f), vec3F(1.0f)); aabb3F bounds(vec3F(0.0f), vec3F(1.0f));
@@ -1274,6 +1297,7 @@ namespace crnlib
high_color = coord; high_color = coord;
} }
// Now optimize the endpoints using the projection bounds on the (potentially skewed) principle axis as a starting point.
if (!optimize_endpoints(low_color, high_color)) if (!optimize_endpoints(low_color, high_color))
return false; return false;
@@ -1286,6 +1310,7 @@ namespace crnlib
return true; return true;
} }
// Tries quantizing the block to 4 colors using vanilla LBG. It tries all combinations of the quantized results as potential endpoints.
bool dxt1_endpoint_optimizer::try_median4(const vec3F& low_color, const vec3F& high_color) bool dxt1_endpoint_optimizer::try_median4(const vec3F& low_color, const vec3F& high_color)
{ {
vec3F means[4]; vec3F means[4];
@@ -1408,6 +1433,8 @@ namespace crnlib
return improved; return improved;
} }
// Given candidate low/high endpoints, find the optimal selectors for 3 and 4 color blocks, compute the resulting error,
// and use the candidate if it results in less error than the best found result so far.
bool dxt1_endpoint_optimizer::evaluate_solution( bool dxt1_endpoint_optimizer::evaluate_solution(
const dxt1_solution_coordinates& coords, const dxt1_solution_coordinates& coords,
bool early_out, bool early_out,
@@ -1428,6 +1455,7 @@ namespace crnlib
CRNLIB_ASSERT(m_trial_solution.m_valid); CRNLIB_ASSERT(m_trial_solution.m_valid);
// Caller has requested all considered candidate solutions for later analysis.
m_pSolutions->resize(m_pSolutions->size() + 1); m_pSolutions->resize(m_pSolutions->size() + 1);
solution& new_solution = m_pSolutions->back(); solution& new_solution = m_pSolutions->back();
new_solution.m_selectors.resize(m_pParams->m_num_pixels); new_solution.m_selectors.resize(m_pParams->m_num_pixels);
@@ -1843,6 +1871,8 @@ namespace crnlib
return unique_color(res, 1); return unique_color(res, 1);
} }
// The block may have been already compressed using another DXTc compressor, such as squish, ATI_Compress, ryg_dxt, etc.
// Attempt to recover the endpoints used by that block compressor.
void dxt1_endpoint_optimizer::try_combinatorial_encoding() void dxt1_endpoint_optimizer::try_combinatorial_encoding()
{ {
if ((m_unique_colors.size() < 2) || (m_unique_colors.size() > 4)) if ((m_unique_colors.size() < 2) || (m_unique_colors.size() > 4))
@@ -1954,6 +1984,8 @@ namespace crnlib
return; return;
} }
// The fourth (transparent) color in 3 color "transparent" blocks is black, which can be optionally exploited for small gains in DXT1 mode if the caller
// doesn't actually use alpha. (But not in DXT5 mode, because 3-color blocks aren't permitted by GPU's for DXT5.)
bool dxt1_endpoint_optimizer::try_alpha_as_black_optimization() bool dxt1_endpoint_optimizer::try_alpha_as_black_optimization()
{ {
const params* pOrig_params = m_pParams; const params* pOrig_params = m_pParams;
@@ -2077,6 +2109,7 @@ namespace crnlib
return true; return true;
} }
// Build array of unique colors and their weights.
void dxt1_endpoint_optimizer::find_unique_colors() void dxt1_endpoint_optimizer::find_unique_colors()
{ {
m_has_transparent_pixels = false; m_has_transparent_pixels = false;
+1 -1
View File
@@ -2504,7 +2504,7 @@ namespace crnlib
{ {
const chunk_tile_desc &tile_desc = encoding_desc.m_tiles[t]; const chunk_tile_desc &tile_desc = encoding_desc.m_tiles[t];
img.draw_box( img.unclipped_fill_box(
x*8 + tile_desc.m_x_ofs, y*8 + tile_desc.m_y_ofs, x*8 + tile_desc.m_x_ofs, y*8 + tile_desc.m_y_ofs,
tile_desc.m_width + 1, tile_desc.m_height + 1, color_quad_u8(128, 128, 128, 255)); tile_desc.m_width + 1, tile_desc.m_height + 1, color_quad_u8(128, 128, 128, 255));
} }
+470 -51
View File
@@ -19,6 +19,10 @@
#include "..\ext\ATI_Compress\ATI_Compress.h" #include "..\ext\ATI_Compress\ATI_Compress.h"
#endif #endif
#include "crn_rg_etc1.h"
#include "crn_etc.h"
#define CRNLIB_USE_RG_ETC1 1
namespace crnlib namespace crnlib
{ {
dxt_image::dxt_image() : dxt_image::dxt_image() :
@@ -103,12 +107,13 @@ namespace crnlib
m_blocks_y = (m_height + 3) >> cDXTBlockShift; m_blocks_y = (m_height + 3) >> cDXTBlockShift;
m_num_elements_per_block = 2; m_num_elements_per_block = 2;
if ((fmt == cDXT1) || (fmt == cDXT1A) || (fmt == cDXT5A)) if ((fmt == cDXT1) || (fmt == cDXT1A) || (fmt == cDXT5A) || (fmt == cETC1))
m_num_elements_per_block = 1; m_num_elements_per_block = 1;
m_total_blocks = m_blocks_x * m_blocks_y; m_total_blocks = m_blocks_x * m_blocks_y;
m_total_elements = m_total_blocks * m_num_elements_per_block; m_total_elements = m_total_blocks * m_num_elements_per_block;
CRNLIB_ASSUME((uint)cDXT1BytesPerBlock == (uint)cETC1BytesPerBlock);
m_bytes_per_block = cDXT1BytesPerBlock * m_num_elements_per_block; m_bytes_per_block = cDXT1BytesPerBlock * m_num_elements_per_block;
m_format = fmt; m_format = fmt;
@@ -118,48 +123,54 @@ namespace crnlib
case cDXT1: case cDXT1:
case cDXT1A: case cDXT1A:
{ {
m_element_type[0] = cColor; m_element_type[0] = cColorDXT1;
m_element_component_index[0] = -1; m_element_component_index[0] = -1;
break; break;
} }
case cDXT3: case cDXT3:
{ {
m_element_type[0] = cAlpha3; m_element_type[0] = cAlphaDXT3;
m_element_type[1] = cColor; m_element_type[1] = cColorDXT1;
m_element_component_index[0] = 3; m_element_component_index[0] = 3;
m_element_component_index[1] = -1; m_element_component_index[1] = -1;
break; break;
} }
case cDXT5: case cDXT5:
{ {
m_element_type[0] = cAlpha5; m_element_type[0] = cAlphaDXT5;
m_element_type[1] = cColor; m_element_type[1] = cColorDXT1;
m_element_component_index[0] = 3; m_element_component_index[0] = 3;
m_element_component_index[1] = -1; m_element_component_index[1] = -1;
break; break;
} }
case cDXT5A: case cDXT5A:
{ {
m_element_type[0] = cAlpha5; m_element_type[0] = cAlphaDXT5;
m_element_component_index[0] = 3; m_element_component_index[0] = 3;
break; break;
} }
case cDXN_XY: case cDXN_XY:
{ {
m_element_type[0] = cAlpha5; m_element_type[0] = cAlphaDXT5;
m_element_type[1] = cAlpha5; m_element_type[1] = cAlphaDXT5;
m_element_component_index[0] = 0; m_element_component_index[0] = 0;
m_element_component_index[1] = 1; m_element_component_index[1] = 1;
break; break;
} }
case cDXN_YX: case cDXN_YX:
{ {
m_element_type[0] = cAlpha5; m_element_type[0] = cAlphaDXT5;
m_element_type[1] = cAlpha5; m_element_type[1] = cAlphaDXT5;
m_element_component_index[0] = 1; m_element_component_index[0] = 1;
m_element_component_index[1] = 0; m_element_component_index[1] = 0;
break; break;
} }
case cETC1:
{
m_element_type[0] = cColorETC1;
m_element_component_index[0] = -1;
break;
}
default: default:
{ {
CRNLIB_ASSERT(0); CRNLIB_ASSERT(0);
@@ -231,8 +242,7 @@ namespace crnlib
uint block_index = 0; uint block_index = 0;
dxt1_endpoint_optimizer dxt1_optimizer; set_block_pixels_context optimizer_context;
dxt5_endpoint_optimizer dxt5_optimizer;
int prev_progress_percentage = -1; int prev_progress_percentage = -1;
for (uint block_y = 0; block_y < m_blocks_y; block_y++) for (uint block_y = 0; block_y < m_blocks_y; block_y++)
@@ -280,7 +290,7 @@ namespace crnlib
} }
} }
set_block_pixels(block_x, block_y, pixels, p, dxt1_optimizer, dxt5_optimizer); set_block_pixels(block_x, block_y, pixels, p, optimizer_context);
} }
} }
} }
@@ -430,6 +440,7 @@ namespace crnlib
for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++) for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
pixels[i].set(0, 0, 0, 255); pixels[i].set(0, 0, 0, 255);
bool all_blocks_valid = true;
for (uint block_y = 0; block_y < m_blocks_y; block_y++) for (uint block_y = 0; block_y < m_blocks_y; block_y++)
{ {
const uint pixel_ofs_y = block_y * cDXTBlockSize; const uint pixel_ofs_y = block_y * cDXTBlockSize;
@@ -437,7 +448,8 @@ namespace crnlib
for (uint block_x = 0; block_x < m_blocks_x; block_x++) for (uint block_x = 0; block_x < m_blocks_x; block_x++)
{ {
get_block_pixels(block_x, block_y, pixels); if (!get_block_pixels(block_x, block_y, pixels))
all_blocks_valid = false;
const uint pixel_ofs_x = block_x * cDXTBlockSize; const uint pixel_ofs_x = block_x * cDXTBlockSize;
@@ -457,6 +469,9 @@ namespace crnlib
} }
} }
if (!all_blocks_valid)
console::error("dxt_image::unpack: One or more invalid blocks encountered!");
img.reset_comp_flags(); img.reset_comp_flags();
img.set_component_valid(0, false); img.set_component_valid(0, false);
img.set_component_valid(1, false); img.set_component_valid(1, false);
@@ -542,7 +557,53 @@ namespace crnlib
{ {
switch (m_element_type[element_index]) switch (m_element_type[element_index])
{ {
case cColor: case cColorETC1:
{
const etc1_block& block = *reinterpret_cast<const etc1_block*>(&get_element(block_x, block_y, element_index));
const bool diff_flag = block.get_diff_bit();
const bool flip_flag = block.get_flip_bit();
const uint table_index0 = block.get_inten_table(0);
const uint table_index1 = block.get_inten_table(1);
color_quad_u8 subblock_colors0[4], subblock_colors1[4];
if (diff_flag)
{
const uint16 base_color5 = block.get_base5_color();
const uint16 delta_color3 = block.get_delta3_color();
etc1_block::get_diff_subblock_colors(subblock_colors0, base_color5, table_index0);
etc1_block::get_diff_subblock_colors(subblock_colors1, base_color5, delta_color3, table_index1);
}
else
{
const uint16 base_color4_0 = block.get_base4_color(0);
etc1_block::get_abs_subblock_colors(subblock_colors0, base_color4_0, table_index0);
const uint16 base_color4_1 = block.get_base4_color(1);
etc1_block::get_abs_subblock_colors(subblock_colors1, base_color4_1, table_index1);
}
const uint bx = x & 3;
const uint by = y & 3;
const uint selector_index = block.get_selector(bx, by);
if (flip_flag)
{
if (by <= 2)
result = subblock_colors0[selector_index];
else
result = subblock_colors1[selector_index];
}
else
{
if (bx <= 2)
result = subblock_colors0[selector_index];
else
result = subblock_colors1[selector_index];
}
break;
}
case cColorDXT1:
{ {
const dxt1_block* pBlock = reinterpret_cast<const dxt1_block*>(&get_element(block_x, block_y, element_index)); const dxt1_block* pBlock = reinterpret_cast<const dxt1_block*>(&get_element(block_x, block_y, element_index));
@@ -584,7 +645,7 @@ namespace crnlib
break; break;
} }
case cAlpha5: case cAlphaDXT5:
{ {
const int comp_index = m_element_component_index[element_index]; const int comp_index = m_element_component_index[element_index];
@@ -626,7 +687,7 @@ namespace crnlib
break; break;
} }
case cAlpha3: case cAlphaDXT3:
{ {
const int comp_index = m_element_component_index[element_index]; const int comp_index = m_element_component_index[element_index];
@@ -652,7 +713,7 @@ namespace crnlib
switch (m_element_type[element_index]) switch (m_element_type[element_index])
{ {
case cColor: case cColorDXT1:
{ {
if (m_format <= cDXT1A) if (m_format <= cDXT1A)
{ {
@@ -675,7 +736,7 @@ namespace crnlib
break; break;
} }
case cAlpha5: case cAlphaDXT5:
{ {
const dxt5_block* pBlock = reinterpret_cast<const dxt5_block*>(&get_element(block_x, block_y, element_index)); const dxt5_block* pBlock = reinterpret_cast<const dxt5_block*>(&get_element(block_x, block_y, element_index));
@@ -713,7 +774,7 @@ namespace crnlib
} }
} }
} }
case cAlpha3: case cAlphaDXT3:
{ {
const dxt3_block* pBlock = reinterpret_cast<const dxt3_block*>(&get_element(block_x, block_y, element_index)); const dxt3_block* pBlock = reinterpret_cast<const dxt3_block*>(&get_element(block_x, block_y, element_index));
@@ -738,7 +799,63 @@ namespace crnlib
{ {
switch (m_element_type[element_index]) switch (m_element_type[element_index])
{ {
case cColor: case cColorETC1:
{
etc1_block& block = *reinterpret_cast<etc1_block*>(&get_element(block_x, block_y, element_index));
const bool diff_flag = block.get_diff_bit();
const bool flip_flag = block.get_flip_bit();
const uint table_index0 = block.get_inten_table(0);
const uint table_index1 = block.get_inten_table(1);
color_quad_u8 subblock_colors0[4], subblock_colors1[4];
if (diff_flag)
{
const uint16 base_color5 = block.get_base5_color();
const uint16 delta_color3 = block.get_delta3_color();
etc1_block::get_diff_subblock_colors(subblock_colors0, base_color5, table_index0);
etc1_block::get_diff_subblock_colors(subblock_colors1, base_color5, delta_color3, table_index1);
}
else
{
const uint16 base_color4_0 = block.get_base4_color(0);
etc1_block::get_abs_subblock_colors(subblock_colors0, base_color4_0, table_index0);
const uint16 base_color4_1 = block.get_base4_color(1);
etc1_block::get_abs_subblock_colors(subblock_colors1, base_color4_1, table_index1);
}
const uint bx = x & 3;
const uint by = y & 3;
color_quad_u8* pColors = subblock_colors1;
if (flip_flag)
{
if (by <= 2)
pColors = subblock_colors0;
}
else
{
if (bx <= 2)
pColors = subblock_colors0;
}
uint best_error = UINT_MAX;
uint best_selector = 0;
for (uint i = 0; i < 4; i++)
{
uint error = color::color_distance(perceptual, pColors[i], c, false);
if (error < best_error)
{
best_error = error;
best_selector = i;
}
}
block.set_selector(bx, by, best_selector);
break;
}
case cColorDXT1:
{ {
dxt1_block* pDXT1_block = reinterpret_cast<dxt1_block*>(pElement); dxt1_block* pDXT1_block = reinterpret_cast<dxt1_block*>(pElement);
@@ -767,7 +884,7 @@ namespace crnlib
break; break;
} }
case cAlpha5: case cAlphaDXT5:
{ {
dxt5_block* pDXT5_block = reinterpret_cast<dxt5_block*>(pElement); dxt5_block* pDXT5_block = reinterpret_cast<dxt5_block*>(pElement);
@@ -794,7 +911,7 @@ namespace crnlib
break; break;
} }
case cAlpha3: case cAlphaDXT3:
{ {
const int comp_index = m_element_component_index[element_index]; const int comp_index = m_element_component_index[element_index];
@@ -809,15 +926,30 @@ namespace crnlib
} // element_index } // element_index
} }
void dxt_image::get_block_pixels(uint block_x, uint block_y, color_quad_u8* pPixels) const bool dxt_image::get_block_pixels(uint block_x, uint block_y, color_quad_u8* pPixels) const
{ {
bool success = true;
const element* pElement = &get_element(block_x, block_y, 0); const element* pElement = &get_element(block_x, block_y, 0);
for (uint element_index = 0; element_index < m_num_elements_per_block; element_index++, pElement++) for (uint element_index = 0; element_index < m_num_elements_per_block; element_index++, pElement++)
{ {
switch (m_element_type[element_index]) switch (m_element_type[element_index])
{ {
case cColor: case cColorETC1:
{
const etc1_block& block = *reinterpret_cast<const etc1_block*>(&get_element(block_x, block_y, element_index));
// Preserve alpha if the format is something weird (like ETC1 for color and DXT5A for alpha) - which isn't currently supported.
#if CRNLIB_USE_RG_ETC1
if (!rg_etc1::unpack_etc1_block(&block, (uint32*)pPixels, m_format != cETC1))
success = false;
#else
if (!unpack_etc1(block, pPixels, m_format != cETC1))
success = false;
#endif
break;
}
case cColorDXT1:
{ {
const dxt1_block* pDXT1_block = reinterpret_cast<const dxt1_block*>(pElement); const dxt1_block* pDXT1_block = reinterpret_cast<const dxt1_block*>(pElement);
@@ -838,7 +970,7 @@ namespace crnlib
break; break;
} }
case cAlpha5: case cAlphaDXT5:
{ {
const dxt5_block* pDXT5_block = reinterpret_cast<const dxt5_block*>(pElement); const dxt5_block* pDXT5_block = reinterpret_cast<const dxt5_block*>(pElement);
@@ -856,7 +988,7 @@ namespace crnlib
break; break;
} }
case cAlpha3: case cAlphaDXT3:
{ {
const dxt3_block* pDXT3_block = reinterpret_cast<const dxt3_block*>(pElement); const dxt3_block* pDXT3_block = reinterpret_cast<const dxt3_block*>(pElement);
@@ -874,21 +1006,53 @@ namespace crnlib
default: break; default: break;
} }
} // element_index } // element_index
return success;
} }
void dxt_image::set_block_pixels(uint block_x, uint block_y, const color_quad_u8* pPixels, const pack_params& p) void dxt_image::set_block_pixels(uint block_x, uint block_y, const color_quad_u8* pPixels, const pack_params& p)
{ {
dxt1_endpoint_optimizer dxt1_optimizer; set_block_pixels_context context;
dxt5_endpoint_optimizer dxt5_optimizer; set_block_pixels(block_x, block_y, pPixels, p, context);
set_block_pixels(block_x, block_y, pPixels, p, dxt1_optimizer, dxt5_optimizer);
} }
void dxt_image::set_block_pixels( void dxt_image::set_block_pixels(
uint block_x, uint block_y, const color_quad_u8* pPixels, const pack_params& p, uint block_x, uint block_y, const color_quad_u8* pPixels, const pack_params& p,
dxt1_endpoint_optimizer& dxt1_optimizer, dxt5_endpoint_optimizer& dxt5_optimizer) set_block_pixels_context& context)
{ {
element* pElement = &get_element(block_x, block_y, 0); element* pElement = &get_element(block_x, block_y, 0);
if (m_format == cETC1)
{
etc1_block &dst_block = *reinterpret_cast<etc1_block*>(pElement);
#if CRNLIB_USE_RG_ETC1
rg_etc1::etc1_quality etc_quality = rg_etc1::cHighQuality;
if (p.m_quality <= cCRNDXTQualityFast)
etc_quality = rg_etc1::cLowQuality;
else if (p.m_quality <= cCRNDXTQualityNormal)
etc_quality = rg_etc1::cMediumQuality;
rg_etc1::etc1_pack_params pack_params;
pack_params.m_dithering = p.m_dithering;
//pack_params.m_perceptual = p.m_perceptual;
pack_params.m_quality = etc_quality;
rg_etc1::pack_etc1_block(&dst_block, (uint32*)pPixels, pack_params);
#else
crn_etc_quality etc_quality = cCRNETCQualitySlow;
if (p.m_quality <= cCRNDXTQualityFast)
etc_quality = cCRNETCQualityFast;
else if (p.m_quality <= cCRNDXTQualityNormal)
etc_quality = cCRNETCQualityMedium;
crn_etc1_pack_params pack_params;
pack_params.m_perceptual = p.m_perceptual;
pack_params.m_quality = etc_quality;
pack_params.m_dithering = p.m_dithering;
pack_etc1_block(dst_block, pPixels, pack_params, context.m_etc1_optimizer);
#endif
}
else
#if CRNLIB_SUPPORT_SQUISH #if CRNLIB_SUPPORT_SQUISH
if ((p.m_compressor == cCRNDXTCompressorSquish) && ((m_format == cDXT1) || (m_format == cDXT1A) || (m_format == cDXT3) || (m_format == cDXT5) || (m_format == cDXT5A))) if ((p.m_compressor == cCRNDXTCompressorSquish) && ((m_format == cDXT1) || (m_format == cDXT1A) || (m_format == cDXT3) || (m_format == cDXT5) || (m_format == cDXT5A)))
{ {
@@ -963,21 +1127,21 @@ namespace crnlib
{ {
switch (m_element_type[element_index]) switch (m_element_type[element_index])
{ {
case cColor: case cColorDXT1:
{ {
dxt1_block* pDXT1_block = reinterpret_cast<dxt1_block*>(pElement); dxt1_block* pDXT1_block = reinterpret_cast<dxt1_block*>(pElement);
dxt_fast::compress_color_block(pDXT1_block, pPixels, p.m_quality >= cCRNDXTQualityNormal); dxt_fast::compress_color_block(pDXT1_block, pPixels, p.m_quality >= cCRNDXTQualityNormal);
break; break;
} }
case cAlpha5: case cAlphaDXT5:
{ {
dxt5_block* pDXT5_block = reinterpret_cast<dxt5_block*>(pElement); dxt5_block* pDXT5_block = reinterpret_cast<dxt5_block*>(pElement);
dxt_fast::compress_alpha_block(pDXT5_block, pPixels, m_element_component_index[element_index]); dxt_fast::compress_alpha_block(pDXT5_block, pPixels, m_element_component_index[element_index]);
break; break;
} }
case cAlpha3: case cAlphaDXT3:
{ {
const int comp_index = m_element_component_index[element_index]; const int comp_index = m_element_component_index[element_index];
@@ -994,11 +1158,14 @@ namespace crnlib
} }
else else
{ {
dxt1_endpoint_optimizer& dxt1_optimizer = context.m_dxt1_optimizer;
dxt5_endpoint_optimizer& dxt5_optimizer = context.m_dxt5_optimizer;
for (uint element_index = 0; element_index < m_num_elements_per_block; element_index++, pElement++) for (uint element_index = 0; element_index < m_num_elements_per_block; element_index++, pElement++)
{ {
switch (m_element_type[element_index]) switch (m_element_type[element_index])
{ {
case cColor: case cColorDXT1:
{ {
dxt1_block* pDXT1_block = reinterpret_cast<dxt1_block*>(pElement); dxt1_block* pDXT1_block = reinterpret_cast<dxt1_block*>(pElement);
@@ -1050,7 +1217,7 @@ namespace crnlib
break; break;
} }
case cAlpha5: case cAlphaDXT5:
{ {
dxt5_block* pDXT5_block = reinterpret_cast<dxt5_block*>(pElement); dxt5_block* pDXT5_block = reinterpret_cast<dxt5_block*>(pElement);
@@ -1081,7 +1248,7 @@ namespace crnlib
break; break;
} }
case cAlpha3: case cAlphaDXT3:
{ {
const int comp_index = m_element_component_index[element_index]; const int comp_index = m_element_component_index[element_index];
@@ -1104,7 +1271,23 @@ namespace crnlib
switch (m_element_type[element_index]) switch (m_element_type[element_index])
{ {
case cColor: case cColorETC1:
{
const etc1_block& src_block = *reinterpret_cast<const etc1_block*>(&block);
if (src_block.get_diff_bit())
{
packed_low_endpoint = src_block.get_base5_color();
packed_high_endpoint = src_block.get_delta3_color();
}
else
{
packed_low_endpoint = src_block.get_base4_color(0);
packed_high_endpoint = src_block.get_base4_color(1);
}
break;
}
case cColorDXT1:
{ {
const dxt1_block& block1 = *reinterpret_cast<const dxt1_block*>(&block); const dxt1_block& block1 = *reinterpret_cast<const dxt1_block*>(&block);
@@ -1113,7 +1296,7 @@ namespace crnlib
break; break;
} }
case cAlpha5: case cAlphaDXT5:
{ {
const dxt5_block& block5 = *reinterpret_cast<const dxt5_block*>(&block); const dxt5_block& block5 = *reinterpret_cast<const dxt5_block*>(&block);
@@ -1122,7 +1305,7 @@ namespace crnlib
break; break;
} }
case cAlpha3: case cAlphaDXT3:
{ {
packed_low_endpoint = 0; packed_low_endpoint = 0;
packed_high_endpoint = 255; packed_high_endpoint = 255;
@@ -1140,7 +1323,23 @@ namespace crnlib
switch (m_element_type[element_index]) switch (m_element_type[element_index])
{ {
case cColor: case cColorETC1:
{
const etc1_block& src_block = *reinterpret_cast<const etc1_block*>(&get_element(block_x, block_y, element_index));
if (src_block.get_diff_bit())
{
low_endpoint = etc1_block::unpack_color5(static_cast<uint16>(l), scaled);
etc1_block::unpack_color5(high_endpoint, static_cast<uint16>(l), static_cast<uint16>(h), scaled);
}
else
{
low_endpoint = etc1_block::unpack_color4(static_cast<uint16>(l), scaled);
high_endpoint = etc1_block::unpack_color4(static_cast<uint16>(h), scaled);
}
return -1;
}
case cColorDXT1:
{ {
uint r, g, b; uint r, g, b;
@@ -1156,7 +1355,7 @@ namespace crnlib
return -1; return -1;
} }
case cAlpha5: case cAlphaDXT5:
{ {
const int component = m_element_component_index[element_index]; const int component = m_element_component_index[element_index];
@@ -1165,7 +1364,7 @@ namespace crnlib
return component; return component;
} }
case cAlpha3: case cAlphaDXT3:
{ {
const int component = m_element_component_index[element_index]; const int component = m_element_component_index[element_index];
@@ -1180,18 +1379,48 @@ namespace crnlib
return 0; return 0;
} }
uint dxt_image::get_block_colors(uint block_x, uint block_y, uint element_index, color_quad_u8* pColors) uint dxt_image::get_block_colors(uint block_x, uint block_y, uint element_index, color_quad_u8* pColors, uint subblock_index)
{ {
const element& block = get_element(block_x, block_y, element_index); const element& block = get_element(block_x, block_y, element_index);
switch (m_element_type[element_index]) switch (m_element_type[element_index])
{ {
case cColor: case cColorETC1:
{
const etc1_block& src_block = *reinterpret_cast<const etc1_block*>(&get_element(block_x, block_y, element_index));
const uint table_index0 = src_block.get_inten_table(0);
const uint table_index1 = src_block.get_inten_table(1);
if (src_block.get_diff_bit())
{
const uint16 base_color5 = src_block.get_base5_color();
const uint16 delta_color3 = src_block.get_delta3_color();
if (subblock_index)
etc1_block::get_diff_subblock_colors(pColors, base_color5, delta_color3, table_index1);
else
etc1_block::get_diff_subblock_colors(pColors, base_color5, table_index0);
}
else
{
if (subblock_index)
{
const uint16 base_color4_1 = src_block.get_base4_color(1);
etc1_block::get_abs_subblock_colors(pColors, base_color4_1, table_index1);
}
else
{
const uint16 base_color4_0 = src_block.get_base4_color(0);
etc1_block::get_abs_subblock_colors(pColors, base_color4_0, table_index0);
}
}
break;
}
case cColorDXT1:
{ {
const dxt1_block& block1 = *reinterpret_cast<const dxt1_block*>(&block); const dxt1_block& block1 = *reinterpret_cast<const dxt1_block*>(&block);
return dxt1_block::get_block_colors(pColors, static_cast<uint16>(block1.get_low_color()), static_cast<uint16>(block1.get_high_color())); return dxt1_block::get_block_colors(pColors, static_cast<uint16>(block1.get_low_color()), static_cast<uint16>(block1.get_high_color()));
} }
case cAlpha5: case cAlphaDXT5:
{ {
const dxt5_block& block5 = *reinterpret_cast<const dxt5_block*>(&block); const dxt5_block& block5 = *reinterpret_cast<const dxt5_block*>(&block);
@@ -1205,7 +1434,7 @@ namespace crnlib
return n; return n;
} }
case cAlpha3: case cAlphaDXT3:
{ {
const int comp_index = m_element_component_index[element_index]; const int comp_index = m_element_component_index[element_index];
for (uint i = 0; i < 16; i++) for (uint i = 0; i < 16; i++)
@@ -1219,6 +1448,32 @@ namespace crnlib
return 0; return 0;
} }
uint dxt_image::get_subblock_index(uint x, uint y, uint element_index) const
{
if (m_element_type[element_index] != cColorETC1)
return 0;
const uint block_x = x >> cDXTBlockShift;
const uint block_y = y >> cDXTBlockShift;
const element& block = get_element(block_x, block_y, element_index);
const etc1_block& src_block = *reinterpret_cast<const etc1_block*>(&block);
if (src_block.get_flip_bit())
{
return ((y & 3) >= 2) ? 1 : 0;
}
else
{
return ((x & 3) >= 2) ? 1 : 0;
}
}
uint dxt_image::get_total_subblocks(uint element_index) const
{
return (m_element_type[element_index] == cColorETC1) ? 2 : 0;
}
uint dxt_image::get_selector(uint x, uint y, uint element_index) const uint dxt_image::get_selector(uint x, uint y, uint element_index) const
{ {
CRNLIB_ASSERT((x < m_width) && (y < m_height)); CRNLIB_ASSERT((x < m_width) && (y < m_height));
@@ -1230,17 +1485,22 @@ namespace crnlib
switch (m_element_type[element_index]) switch (m_element_type[element_index])
{ {
case cColor: case cColorETC1:
{
const etc1_block& src_block = *reinterpret_cast<const etc1_block*>(&block);
return src_block.get_selector(x & 3, y & 3);
}
case cColorDXT1:
{ {
const dxt1_block& block1 = *reinterpret_cast<const dxt1_block*>(&block); const dxt1_block& block1 = *reinterpret_cast<const dxt1_block*>(&block);
return block1.get_selector(x & 3, y & 3); return block1.get_selector(x & 3, y & 3);
} }
case cAlpha5: case cAlphaDXT5:
{ {
const dxt5_block& block5 = *reinterpret_cast<const dxt5_block*>(&block); const dxt5_block& block5 = *reinterpret_cast<const dxt5_block*>(&block);
return block5.get_selector(x & 3, y & 3); return block5.get_selector(x & 3, y & 3);
} }
case cAlpha3: case cAlphaDXT3:
{ {
const dxt3_block& block3 = *reinterpret_cast<const dxt3_block*>(&block); const dxt3_block& block3 = *reinterpret_cast<const dxt3_block*>(&block);
return block3.get_alpha(x & 3, y & 3, false); return block3.get_alpha(x & 3, y & 3, false);
@@ -1257,6 +1517,165 @@ namespace crnlib
m_format = cDXT1A; m_format = cDXT1A;
} }
void dxt_image::flip_col(uint x)
{
const uint other_x = (m_blocks_x - 1) - x;
for (uint y = 0; y < m_blocks_y; y++)
{
for (uint e = 0; e < get_elements_per_block(); e++)
{
element tmp[2] = { get_element(x, y, e), get_element(other_x, y, e) };
for (uint i = 0; i < 2; i++)
{
switch (get_element_type(e))
{
case cColorDXT1: reinterpret_cast<dxt1_block*>(&tmp[i])->flip_x(); break;
case cAlphaDXT3: reinterpret_cast<dxt3_block*>(&tmp[i])->flip_x(); break;
case cAlphaDXT5: reinterpret_cast<dxt5_block*>(&tmp[i])->flip_x(); break;
default: CRNLIB_ASSERT(0); break;
}
}
get_element(x, y, e) = tmp[1];
get_element(other_x, y, e) = tmp[0];
}
}
}
void dxt_image::flip_row(uint y)
{
const uint other_y = (m_blocks_y - 1) - y;
for (uint x = 0; x < m_blocks_x; x++)
{
for (uint e = 0; e < get_elements_per_block(); e++)
{
element tmp[2] = { get_element(x, y, e), get_element(x, other_y, e) };
for (uint i = 0; i < 2; i++)
{
switch (get_element_type(e))
{
case cColorDXT1: reinterpret_cast<dxt1_block*>(&tmp[i])->flip_y(); break;
case cAlphaDXT3: reinterpret_cast<dxt3_block*>(&tmp[i])->flip_y(); break;
case cAlphaDXT5: reinterpret_cast<dxt5_block*>(&tmp[i])->flip_y(); break;
default: CRNLIB_ASSERT(0); break;
}
}
get_element(x, y, e) = tmp[1];
get_element(x, other_y, e) = tmp[0];
}
}
}
bool dxt_image::can_flip(uint axis_index)
{
if (m_format == cETC1)
{
// Can't reliably flip ETC1 textures (because of asymmetry in the 555/333 differential coding of subblock colors).
return false;
}
uint d;
if (axis_index)
d = m_height;
else
d = m_width;
if (d & 3)
{
if (d > 4)
return false;
}
return true;
}
bool dxt_image::flip_x()
{
if (m_format == cETC1)
{
// Can't reliably flip ETC1 textures (because of asymmetry in the 555/333 differential coding of subblock colors).
return false;
}
if ((m_width & 3) && (m_width > 4))
return false;
if (m_width == 1)
return true;
const uint mid_x = m_blocks_x / 2;
for (uint x = 0; x < mid_x; x++)
flip_col(x);
if (m_blocks_x & 1)
{
const uint w = math::minimum(m_width, 4U);
for (uint y = 0; y < m_blocks_y; y++)
{
for (uint e = 0; e < get_elements_per_block(); e++)
{
element tmp(get_element(mid_x, y, e));
switch (get_element_type(e))
{
case cColorDXT1: reinterpret_cast<dxt1_block*>(&tmp)->flip_x(w, 4); break;
case cAlphaDXT3: reinterpret_cast<dxt3_block*>(&tmp)->flip_x(w, 4); break;
case cAlphaDXT5: reinterpret_cast<dxt5_block*>(&tmp)->flip_x(w, 4); break;
default: CRNLIB_ASSERT(0); break;
}
get_element(mid_x, y, e) = tmp;
}
}
}
return true;
}
bool dxt_image::flip_y()
{
if (m_format == cETC1)
{
// Can't reliably flip ETC1 textures (because of asymmetry in the 555/333 differential coding of subblock colors).
return false;
}
if ((m_height & 3) && (m_height > 4))
return false;
if (m_height == 1)
return true;
const uint mid_y = m_blocks_y / 2;
for (uint y = 0; y < mid_y; y++)
flip_row(y);
if (m_blocks_y & 1)
{
const uint h = math::minimum(m_height, 4U);
for (uint x = 0; x < m_blocks_x; x++)
{
for (uint e = 0; e < get_elements_per_block(); e++)
{
element tmp(get_element(x, mid_y, e));
switch (get_element_type(e))
{
case cColorDXT1: reinterpret_cast<dxt1_block*>(&tmp)->flip_y(4, h); break;
case cAlphaDXT3: reinterpret_cast<dxt3_block*>(&tmp)->flip_y(4, h); break;
case cAlphaDXT5: reinterpret_cast<dxt5_block*>(&tmp)->flip_y(4, h); break;
default: CRNLIB_ASSERT(0); break;
}
get_element(x, mid_y, e) = tmp;
}
}
}
return true;
}
} // namespace crnlib } // namespace crnlib
+39 -9
View File
@@ -3,6 +3,10 @@
#pragma once #pragma once
#include "crn_dxt1.h" #include "crn_dxt1.h"
#include "crn_dxt5a.h" #include "crn_dxt5a.h"
#include "crn_etc.h"
#if CRNLIB_SUPPORT_ETC_A1
#include "crn_etc_a1.h"
#endif
#include "crn_image.h" #include "crn_image.h"
#define CRNLIB_SUPPORT_ATI_COMPRESS 0 #define CRNLIB_SUPPORT_ATI_COMPRESS 0
@@ -34,7 +38,7 @@ namespace crnlib
dxt_format get_format() const { return m_format; } 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); } 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. // Will be pretty slow if the image is DXT1, as this method scans for alpha blocks/selectors.
bool has_alpha() const; bool has_alpha() const;
@@ -43,10 +47,12 @@ namespace crnlib
{ {
cUnused = 0, cUnused = 0,
cColor, cColorDXT1, // DXT1 color block
cAlpha3, cAlphaDXT3, // DXT3 alpha block (only)
cAlpha5, 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]; } element_type get_element_type(uint element_index) const { CRNLIB_ASSERT(element_index < m_num_elements_per_block); return m_element_type[element_index]; }
@@ -86,6 +92,7 @@ namespace crnlib
{ {
m_quality = cCRNDXTQualityUber; m_quality = cCRNDXTQualityUber;
m_perceptual = true; m_perceptual = true;
m_dithering = false;
m_grayscale_sampling = false; m_grayscale_sampling = false;
m_use_both_block_types = true; m_use_both_block_types = true;
m_endpoint_caching = true; m_endpoint_caching = true;
@@ -125,6 +132,7 @@ namespace crnlib
crn_dxt_compressor_type m_compressor; crn_dxt_compressor_type m_compressor;
bool m_perceptual; bool m_perceptual;
bool m_dithering;
bool m_grayscale_sampling; bool m_grayscale_sampling;
bool m_use_both_block_types; bool m_use_both_block_types;
bool m_endpoint_caching; bool m_endpoint_caching;
@@ -148,7 +156,7 @@ namespace crnlib
void endian_swap(); void endian_swap();
uint get_num_elements() const { return m_elements.size(); } uint get_total_elements() const { return m_elements.size(); }
const element_vec& get_element_vec() const { return m_elements; } const element_vec& get_element_vec() const { return m_elements; }
element_vec& get_element_vec() { return m_elements; } element_vec& get_element_vec() { return m_elements; }
@@ -168,9 +176,19 @@ namespace crnlib
void set_pixel(uint x, uint y, const color_quad_u8& c, bool perceptual = true); 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! // get_block_pixels() only sets those components stored in the image!
void get_block_pixels(uint block_x, uint block_y, color_quad_u8* pPixels) const; 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, dxt1_endpoint_optimizer& dxt1_optimizer, dxt5_endpoint_optimizer& dxt5_optimizer); 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 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; void get_block_endpoints(uint block_x, uint block_y, uint element_index, uint& packed_low_endpoint, uint& packed_high_endpoint) const;
@@ -181,11 +199,20 @@ namespace crnlib
// pColors should point to a 16 entry array, to handle DXT3. // pColors should point to a 16 entry array, to handle DXT3.
// Returns the number of block colors: 3, 4, 6, 8, or 16. // 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 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; uint get_selector(uint x, uint y, uint element_index) const;
void change_dxt1_to_dxt1a(); 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: private:
element_vec m_elements; element_vec m_elements;
@@ -205,7 +232,7 @@ namespace crnlib
int8 m_element_component_index[2]; int8 m_element_component_index[2];
element_type m_element_type[2]; element_type m_element_type[2];
dxt_format m_format; // DXT1, 1A, 3, 5, N/3DC, or A dxt_format m_format; // DXT1, 1A, 3, 5, N/3DC, or 5A
bool init_internal(dxt_format fmt, uint width, uint height); bool init_internal(dxt_format fmt, uint width, uint height);
void init_task(uint64 data, void* pData_ptr); void init_task(uint64 data, void* pData_ptr);
@@ -213,6 +240,9 @@ namespace crnlib
#if CRNLIB_SUPPORT_ATI_COMPRESS #if CRNLIB_SUPPORT_ATI_COMPRESS
bool init_ati_compress(dxt_format fmt, const image_u8& img, const pack_params& p); bool init_ati_compress(dxt_format fmt, const image_u8& img, const pack_params& p);
#endif #endif
void flip_col(uint x);
void flip_row(uint y);
}; };
} // namespace crnlib } // namespace crnlib
+1481
View File
File diff suppressed because it is too large Load Diff
+609
View File
@@ -0,0 +1,609 @@
// File: crn_etc.h
// See Copyright Notice and license at the end of inc/crnlib.h
#pragma once
#include "../inc/crnlib.h"
#include "crn_dxt.h"
namespace crnlib
{
enum etc_constants
{
cETC1BytesPerBlock = 8U,
cETC1SelectorBits = 2U,
cETC1SelectorValues = 1U << cETC1SelectorBits,
cETC1SelectorMask = cETC1SelectorValues - 1U,
cETC1BlockShift = 2U,
cETC1BlockSize = 1U << cETC1BlockShift,
cETC1LSBSelectorIndicesBitOffset = 0,
cETC1MSBSelectorIndicesBitOffset = 16,
cETC1FlipBitOffset = 32,
cETC1DiffBitOffset = 33,
cETC1IntenModifierNumBits = 3,
cETC1IntenModifierValues = 1 << cETC1IntenModifierNumBits,
cETC1RightIntenModifierTableBitOffset = 34,
cETC1LeftIntenModifierTableBitOffset = 37,
// Base+Delta encoding (5 bit bases, 3 bit delta)
cETC1BaseColorCompNumBits = 5,
cETC1BaseColorCompMax = 1 << cETC1BaseColorCompNumBits,
cETC1DeltaColorCompNumBits = 3,
cETC1DeltaColorComp = 1 << cETC1DeltaColorCompNumBits,
cETC1DeltaColorCompMax = 1 << cETC1DeltaColorCompNumBits,
cETC1BaseColor5RBitOffset = 59,
cETC1BaseColor5GBitOffset = 51,
cETC1BaseColor5BBitOffset = 43,
cETC1DeltaColor3RBitOffset = 56,
cETC1DeltaColor3GBitOffset = 48,
cETC1DeltaColor3BBitOffset = 40,
// Absolute (non-delta) encoding (two 4-bit per component bases)
cETC1AbsColorCompNumBits = 4,
cETC1AbsColorCompMax = 1 << cETC1AbsColorCompNumBits,
cETC1AbsColor4R1BitOffset = 60,
cETC1AbsColor4G1BitOffset = 52,
cETC1AbsColor4B1BitOffset = 44,
cETC1AbsColor4R2BitOffset = 56,
cETC1AbsColor4G2BitOffset = 48,
cETC1AbsColor4B2BitOffset = 40,
cETC1ColorDeltaMin = -4,
cETC1ColorDeltaMax = 3,
// Delta3:
// 0 1 2 3 4 5 6 7
// 000 001 010 011 100 101 110 111
// 0 1 2 3 -4 -3 -2 -1
};
extern const int g_etc1_inten_tables[cETC1IntenModifierValues][cETC1SelectorValues];
extern const uint8 g_etc1_to_selector_index[cETC1SelectorValues];
extern const uint8 g_selector_index_to_etc1[cETC1SelectorValues];
struct etc1_coord2
{
uint8 m_x, m_y;
};
extern const etc1_coord2 g_etc1_pixel_coords[2][2][8]; // [flipped][subblock][subblock_pixel]
struct etc1_block
{
// big endian uint64:
// bit ofs: 56 48 40 32 24 16 8 0
// byte ofs: b0, b1, b2, b3, b4, b5, b6, b7
union
{
uint64 m_uint64;
uint8 m_bytes[8];
};
uint8 m_low_color[2];
uint8 m_high_color[2];
enum { cNumSelectorBytes = 4 };
uint8 m_selectors[cNumSelectorBytes];
inline void clear()
{
utils::zero_this(this);
}
inline uint get_general_bits(uint ofs, uint num) const
{
CRNLIB_ASSERT((ofs + num) <= 64U);
CRNLIB_ASSERT(num && (num < 32U));
return (utils::read_be64(&m_uint64) >> ofs) & ((1UL << num) - 1UL);
}
inline void set_general_bits(uint ofs, uint num, uint bits)
{
CRNLIB_ASSERT((ofs + num) <= 64U);
CRNLIB_ASSERT(num && (num < 32U));
uint64 x = utils::read_be64(&m_uint64);
uint64 msk = ((1ULL << static_cast<uint64>(num)) - 1ULL) << static_cast<uint64>(ofs);
x &= ~msk;
x |= (static_cast<uint64>(bits) << static_cast<uint64>(ofs));
utils::write_be64(&m_uint64, x);
}
inline uint get_byte_bits(uint ofs, uint num) const
{
CRNLIB_ASSERT((ofs + num) <= 64U);
CRNLIB_ASSERT(num && (num <= 8U));
CRNLIB_ASSERT((ofs >> 3) == ((ofs + num - 1) >> 3));
const uint byte_ofs = 7 - (ofs >> 3);
const uint byte_bit_ofs = ofs & 7;
return (m_bytes[byte_ofs] >> byte_bit_ofs) & ((1 << num) - 1);
}
inline void set_byte_bits(uint ofs, uint num, uint bits)
{
CRNLIB_ASSERT((ofs + num) <= 64U);
CRNLIB_ASSERT(num && (num < 32U));
CRNLIB_ASSERT((ofs >> 3) == ((ofs + num - 1) >> 3));
CRNLIB_ASSERT(bits < (1U << num));
const uint byte_ofs = 7 - (ofs >> 3);
const uint byte_bit_ofs = ofs & 7;
const uint mask = (1 << num) - 1;
m_bytes[byte_ofs] &= ~(mask << byte_bit_ofs);
m_bytes[byte_ofs] |= (bits << byte_bit_ofs);
}
// false = left/right subblocks
// true = upper/lower subblocks
inline bool get_flip_bit() const
{
return (m_bytes[3] & 1) != 0;
}
inline void set_flip_bit(bool flip)
{
m_bytes[3] &= ~1;
m_bytes[3] |= static_cast<uint8>(flip);
}
inline bool get_diff_bit() const
{
return (m_bytes[3] & 2) != 0;
}
inline void set_diff_bit(bool diff)
{
m_bytes[3] &= ~2;
m_bytes[3] |= (static_cast<uint>(diff) << 1);
}
// Returns intensity modifier table (0-7) used by subblock subblock_id.
// subblock_id=0 left/top (CW 1), 1=right/bottom (CW 2)
inline uint get_inten_table(uint subblock_id) const
{
CRNLIB_ASSERT(subblock_id < 2);
const uint ofs = subblock_id ? 2 : 5;
return (m_bytes[3] >> ofs) & 7;
}
// Sets intensity modifier table (0-7) used by subblock subblock_id (0 or 1)
inline void set_inten_table(uint subblock_id, uint t)
{
CRNLIB_ASSERT(subblock_id < 2);
CRNLIB_ASSERT(t < 8);
const uint ofs = subblock_id ? 2 : 5;
m_bytes[3] &= ~(7 << ofs);
m_bytes[3] |= (t << ofs);
}
// Returned selector value ranges from 0-3 and is a direct index into g_etc1_inten_tables.
inline uint get_selector(uint x, uint y) const
{
CRNLIB_ASSERT((x | y) < 4);
const uint bit_index = x * 4 + y;
const uint byte_bit_ofs = bit_index & 7;
const uint8 *p = &m_bytes[7 - (bit_index >> 3)];
const uint lsb = (p[0] >> byte_bit_ofs) & 1;
const uint msb = (p[-2] >> byte_bit_ofs) & 1;
const uint val = lsb | (msb << 1);
return g_etc1_to_selector_index[val];
}
// Selector "val" ranges from 0-3 and is a direct index into g_etc1_inten_tables.
inline void set_selector(uint x, uint y, uint val)
{
CRNLIB_ASSERT((x | y | val) < 4);
const uint bit_index = x * 4 + y;
uint8 *p = &m_bytes[7 - (bit_index >> 3)];
const uint byte_bit_ofs = bit_index & 7;
const uint mask = 1 << byte_bit_ofs;
const uint etc1_val = g_selector_index_to_etc1[val];
const uint lsb = etc1_val & 1;
const uint msb = etc1_val >> 1;
p[0] &= ~mask;
p[0] |= (lsb << byte_bit_ofs);
p[-2] &= ~mask;
p[-2] |= (msb << byte_bit_ofs);
}
inline void set_base4_color(uint idx, uint16 c)
{
if (idx)
{
set_byte_bits(cETC1AbsColor4R2BitOffset, 4, (c >> 8) & 15);
set_byte_bits(cETC1AbsColor4G2BitOffset, 4, (c >> 4) & 15);
set_byte_bits(cETC1AbsColor4B2BitOffset, 4, c & 15);
}
else
{
set_byte_bits(cETC1AbsColor4R1BitOffset, 4, (c >> 8) & 15);
set_byte_bits(cETC1AbsColor4G1BitOffset, 4, (c >> 4) & 15);
set_byte_bits(cETC1AbsColor4B1BitOffset, 4, c & 15);
}
}
inline uint16 get_base4_color(uint idx) const
{
uint r, g, b;
if (idx)
{
r = get_byte_bits(cETC1AbsColor4R2BitOffset, 4);
g = get_byte_bits(cETC1AbsColor4G2BitOffset, 4);
b = get_byte_bits(cETC1AbsColor4B2BitOffset, 4);
}
else
{
r = get_byte_bits(cETC1AbsColor4R1BitOffset, 4);
g = get_byte_bits(cETC1AbsColor4G1BitOffset, 4);
b = get_byte_bits(cETC1AbsColor4B1BitOffset, 4);
}
return static_cast<uint16>(b | (g << 4U) | (r << 8U));
}
inline void set_base5_color(uint16 c)
{
set_byte_bits(cETC1BaseColor5RBitOffset, 5, (c >> 10) & 31);
set_byte_bits(cETC1BaseColor5GBitOffset, 5, (c >> 5) & 31);
set_byte_bits(cETC1BaseColor5BBitOffset, 5, c & 31);
}
inline uint16 get_base5_color() const
{
const uint r = get_byte_bits(cETC1BaseColor5RBitOffset, 5);
const uint g = get_byte_bits(cETC1BaseColor5GBitOffset, 5);
const uint b = get_byte_bits(cETC1BaseColor5BBitOffset, 5);
return static_cast<uint16>(b | (g << 5U) | (r << 10U));
}
void set_delta3_color(uint16 c)
{
set_byte_bits(cETC1DeltaColor3RBitOffset, 3, (c >> 6) & 7);
set_byte_bits(cETC1DeltaColor3GBitOffset, 3, (c >> 3) & 7);
set_byte_bits(cETC1DeltaColor3BBitOffset, 3, c & 7);
}
inline uint16 get_delta3_color() const
{
const uint r = get_byte_bits(cETC1DeltaColor3RBitOffset, 3);
const uint g = get_byte_bits(cETC1DeltaColor3GBitOffset, 3);
const uint b = get_byte_bits(cETC1DeltaColor3BBitOffset, 3);
return static_cast<uint16>(b | (g << 3U) | (r << 6U));
}
// Base color 5
static uint16 pack_color5(const color_quad_u8& color, bool scaled, uint bias = 127U);
static uint16 pack_color5(uint r, uint g, uint b, bool scaled, uint bias = 127U);
static color_quad_u8 unpack_color5(uint16 packed_color5, bool scaled, uint alpha = 255U);
static void unpack_color5(uint& r, uint& g, uint& b, uint16 packed_color, bool scaled);
static bool unpack_color5(color_quad_u8& result, uint16 packed_color5, uint16 packed_delta3, bool scaled, uint alpha = 255U);
static bool unpack_color5(uint& r, uint& g, uint& b, uint16 packed_color5, uint16 packed_delta3, bool scaled, uint alpha = 255U);
// Delta color 3
// Inputs range from -4 to 3 (cETC1ColorDeltaMin to cETC1ColorDeltaMax)
static uint16 pack_delta3(const color_quad_i16& color);
static uint16 pack_delta3(int r, int g, int b);
// Results range from -4 to 3 (cETC1ColorDeltaMin to cETC1ColorDeltaMax)
static color_quad_i16 unpack_delta3(uint16 packed_delta3);
static void unpack_delta3(int& r, int& g, int& b, uint16 packed_delta3);
// Abs color 4
static uint16 pack_color4(const color_quad_u8& color, bool scaled, uint bias = 127U);
static uint16 pack_color4(uint r, uint g, uint b, bool scaled, uint bias = 127U);
static color_quad_u8 unpack_color4(uint16 packed_color4, bool scaled, uint alpha = 255U);
static void unpack_color4(uint& r, uint& g, uint& b, uint16 packed_color4, bool scaled);
// subblock colors
static void get_diff_subblock_colors(color_quad_u8* pDst, uint16 packed_color5, uint table_idx);
static bool get_diff_subblock_colors(color_quad_u8* pDst, uint16 packed_color5, uint16 packed_delta3, uint table_idx);
static void get_abs_subblock_colors(color_quad_u8* pDst, uint16 packed_color4, uint table_idx);
static inline void unscaled_to_scaled_color(color_quad_u8& dst, const color_quad_u8& src, bool color4)
{
if (color4)
{
dst.r = src.r | (src.r << 4);
dst.g = src.g | (src.g << 4);
dst.b = src.b | (src.b << 4);
}
else
{
dst.r = (src.r >> 2) | (src.r << 3);
dst.g = (src.g >> 2) | (src.g << 3);
dst.b = (src.b >> 2) | (src.b << 3);
}
dst.a = src.a;
}
};
CRNLIB_DEFINE_BITWISE_COPYABLE(etc1_block);
// Returns false if the block is invalid (it will still be unpacked with clamping).
bool unpack_etc1(const etc1_block& block, color_quad_u8 *pDst, bool preserve_alpha = false);
enum crn_etc_quality
{
cCRNETCQualityFast,
cCRNETCQualityMedium,
cCRNETCQualitySlow,
cCRNETCQualityTotal,
cCRNETCQualityForceDWORD = 0xFFFFFFFF
};
struct crn_etc1_pack_params
{
crn_etc_quality m_quality;
bool m_perceptual;
bool m_dithering;
inline crn_etc1_pack_params()
{
clear();
}
void clear()
{
m_quality = cCRNETCQualitySlow;
m_perceptual = true;
m_dithering = false;
}
};
struct etc1_solution_coordinates
{
inline etc1_solution_coordinates() :
m_unscaled_color(0, 0, 0, 0),
m_inten_table(0),
m_color4(false)
{
}
inline etc1_solution_coordinates(uint r, uint g, uint b, uint inten_table, bool color4) :
m_unscaled_color(r, g, b, 255),
m_inten_table(inten_table),
m_color4(color4)
{
}
inline etc1_solution_coordinates(const color_quad_u8& c, uint inten_table, bool color4) :
m_unscaled_color(c),
m_inten_table(inten_table),
m_color4(color4)
{
}
inline etc1_solution_coordinates(const etc1_solution_coordinates& other)
{
*this = other;
}
inline etc1_solution_coordinates& operator= (const etc1_solution_coordinates& rhs)
{
m_unscaled_color = rhs.m_unscaled_color;
m_inten_table = rhs.m_inten_table;
m_color4 = rhs.m_color4;
return *this;
}
inline void clear()
{
m_unscaled_color.clear();
m_inten_table = 0;
m_color4 = false;
}
inline color_quad_u8 get_scaled_color() const
{
int br, bg, bb;
if (m_color4)
{
br = m_unscaled_color.r | (m_unscaled_color.r << 4);
bg = m_unscaled_color.g | (m_unscaled_color.g << 4);
bb = m_unscaled_color.b | (m_unscaled_color.b << 4);
}
else
{
br = (m_unscaled_color.r >> 2) | (m_unscaled_color.r << 3);
bg = (m_unscaled_color.g >> 2) | (m_unscaled_color.g << 3);
bb = (m_unscaled_color.b >> 2) | (m_unscaled_color.b << 3);
}
return color_quad_u8(br, bg, bb);
}
inline void get_block_colors(color_quad_u8* pBlock_colors)
{
int br, bg, bb;
if (m_color4)
{
br = m_unscaled_color.r | (m_unscaled_color.r << 4);
bg = m_unscaled_color.g | (m_unscaled_color.g << 4);
bb = m_unscaled_color.b | (m_unscaled_color.b << 4);
}
else
{
br = (m_unscaled_color.r >> 2) | (m_unscaled_color.r << 3);
bg = (m_unscaled_color.g >> 2) | (m_unscaled_color.g << 3);
bb = (m_unscaled_color.b >> 2) | (m_unscaled_color.b << 3);
}
const int* pInten_table = g_etc1_inten_tables[m_inten_table];
pBlock_colors[0].set(br + pInten_table[0], bg + pInten_table[0], bb + pInten_table[0]);
pBlock_colors[1].set(br + pInten_table[1], bg + pInten_table[1], bb + pInten_table[1]);
pBlock_colors[2].set(br + pInten_table[2], bg + pInten_table[2], bb + pInten_table[2]);
pBlock_colors[3].set(br + pInten_table[3], bg + pInten_table[3], bb + pInten_table[3]);
}
color_quad_u8 m_unscaled_color;
uint m_inten_table;
bool m_color4;
};
class etc1_optimizer
{
CRNLIB_NO_COPY_OR_ASSIGNMENT_OP(etc1_optimizer);
public:
etc1_optimizer()
{
clear();
}
void clear()
{
m_pParams = NULL;
m_pResult = NULL;
m_pSorted_luma = NULL;
m_pSorted_luma_indices = NULL;
}
struct params : crn_etc1_pack_params
{
params()
{
clear();
}
params(const crn_etc1_pack_params& base_params) :
crn_etc1_pack_params(base_params)
{
clear_optimizer_params();
}
void clear()
{
crn_etc1_pack_params::clear();
clear_optimizer_params();
}
void clear_optimizer_params()
{
m_num_src_pixels = 0;
m_pSrc_pixels = 0;
m_use_color4 = false;
static const int s_default_scan_delta[] = { 0 };
m_pScan_deltas = s_default_scan_delta;
m_scan_delta_size = 1;
m_base_color5.clear();
m_constrain_against_base_color5 = false;
}
uint m_num_src_pixels;
const color_quad_u8* m_pSrc_pixels;
bool m_use_color4;
const int* m_pScan_deltas;
uint m_scan_delta_size;
color_quad_u8 m_base_color5;
bool m_constrain_against_base_color5;
};
struct results
{
uint64 m_error;
color_quad_u8 m_block_color_unscaled;
uint m_block_inten_table;
uint m_n;
uint8* m_pSelectors;
bool m_block_color4;
inline results& operator= (const results& rhs)
{
m_block_color_unscaled = rhs.m_block_color_unscaled;
m_block_color4 = rhs.m_block_color4;
m_block_inten_table = rhs.m_block_inten_table;
m_error = rhs.m_error;
CRNLIB_ASSERT(m_n == rhs.m_n);
memcpy(m_pSelectors, rhs.m_pSelectors, rhs.m_n);
return *this;
}
};
void init(const params& params, results& result);
bool compute();
private:
struct potential_solution
{
potential_solution() : m_coords(), m_error(cUINT64_MAX), m_valid(false)
{
}
etc1_solution_coordinates m_coords;
crnlib::vector<uint8> m_selectors;
uint64 m_error;
bool m_valid;
void clear()
{
m_coords.clear();
m_selectors.resize(0);
m_error = cUINT64_MAX;
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;
}
};
const params* m_pParams;
results* m_pResult;
int m_limit;
vec3F m_avg_color;
int m_br, m_bg, m_bb;
crnlib::vector<uint16> m_luma;
crnlib::vector<uint32> m_sorted_luma[2];
const uint32* m_pSorted_luma_indices;
uint32* m_pSorted_luma;
crnlib::vector<uint8> m_selectors;
crnlib::vector<uint8> m_best_selectors;
potential_solution m_best_solution;
potential_solution m_trial_solution;
crnlib::vector<uint8> m_temp_selectors;
bool evaluate_solution(const etc1_solution_coordinates& coords, potential_solution& trial_solution, potential_solution* pBest_solution);
bool evaluate_solution_fast(const etc1_solution_coordinates& coords, potential_solution& trial_solution, potential_solution* pBest_solution);
};
struct pack_etc1_block_context
{
etc1_optimizer m_optimizer;
};
void pack_etc1_block_init();
uint64 pack_etc1_block(etc1_block& block, const color_quad_u8* pSrc_pixels, crn_etc1_pack_params& pack_params, pack_etc1_block_context& context);
} // namespace crnlib
+21
View File
@@ -554,4 +554,25 @@ namespace crnlib
return !*pWild; return !*pWild;
} }
bool file_utils::write_buf_to_file(const char* pPath, const void* pData, size_t data_size)
{
FILE *pFile = NULL;
#ifdef _MSC_VER
// Compiling with MSVC
if (fopen_s(&pFile, pPath, "wb"))
return false;
#else
pFile = fopen(pPath, "wb");
#endif
if (!pFile)
return false;
bool success = fwrite(pData, 1, data_size, pFile) == data_size;
fclose(pFile);
return success;
}
} // namespace crnlib } // namespace crnlib
+2 -7
View File
@@ -20,29 +20,24 @@ namespace crnlib
static bool is_drive_separator(char c); static bool is_drive_separator(char c);
static bool split_path(const char* p, dynamic_string* pDrive, dynamic_string* pDir, dynamic_string* pFilename, dynamic_string* pExt); static bool split_path(const char* p, dynamic_string* pDrive, dynamic_string* pDir, dynamic_string* pFilename, dynamic_string* pExt);
static bool split_path(const char* p, dynamic_string& path, dynamic_string& filename); static bool split_path(const char* p, dynamic_string& path, dynamic_string& filename);
static bool get_pathname(const char* p, dynamic_string& path); static bool get_pathname(const char* p, dynamic_string& path);
static bool get_filename(const char* p, dynamic_string& filename); static bool get_filename(const char* p, dynamic_string& filename);
static void combine_path(dynamic_string& dst, const char* pA, const char* pB); static void combine_path(dynamic_string& dst, const char* pA, const char* pB);
static void combine_path(dynamic_string& dst, const char* pA, const char* pB, const char* pC); static void combine_path(dynamic_string& dst, const char* pA, const char* pB, const char* pC);
static bool full_path(dynamic_string& path); static bool full_path(dynamic_string& path);
static bool get_extension(dynamic_string& filename); static bool get_extension(dynamic_string& filename);
static bool remove_extension(dynamic_string& filename); static bool remove_extension(dynamic_string& filename);
static bool create_path(const dynamic_string& path); static bool create_path(const dynamic_string& path);
static void trim_trailing_seperator(dynamic_string& path); static void trim_trailing_seperator(dynamic_string& path);
static int wildcmp(const char* pWild, const char* pString); static int wildcmp(const char* pWild, const char* pString);
static bool write_buf_to_file(const char* pPath, const void* pData, size_t data_size);
}; // struct file_utils }; // struct file_utils
} // namespace crnlib } // namespace crnlib
+9 -4
View File
@@ -2,8 +2,9 @@
// See Copyright Notice and license at the end of inc/crnlib.h // See Copyright Notice and license at the end of inc/crnlib.h
// //
// Notes: // Notes:
// stl-like hash map/hash set, with predictable performance across platforms/compilers/C run times/etc.
// Hash function ref: http://www.brpreiss.com/books/opus4/html/page215.html // Hash function ref: http://www.brpreiss.com/books/opus4/html/page215.html
// Compared for speed against VC9's std::hash_map. // Compared for performance against VC9's std::hash_map.
// Linear probing, auto resizes on ~50% load factor. // Linear probing, auto resizes on ~50% load factor.
// Uses Knuth's multiplicative method (Fibonacci hashing). // Uses Knuth's multiplicative method (Fibonacci hashing).
#pragma once #pragma once
@@ -374,6 +375,8 @@ namespace crnlib
return iterator(*this, m_values.size()); return iterator(*this, m_values.size());
} }
// insert_result.first will always point to inserted key/value (or the already existing key/value).
// insert_resutt.second will be true if a new key/value was inserted, or false if the key already existed (in which case first will point to the already existing value).
typedef std::pair<iterator, bool> insert_result; typedef std::pair<iterator, bool> insert_result;
inline insert_result insert(const Key& k, const Value& v = Value()) inline insert_result insert(const Key& k, const Value& v = Value())
@@ -508,17 +511,19 @@ namespace crnlib
scalar_type<Value>::destruct(&p->second); scalar_type<Value>::destruct(&p->second);
} }
// Moves *pSrc to *pDst efficiently.
// pDst should NOT be constructed on entry.
static inline void move_node(node* pDst, node* pSrc) static inline void move_node(node* pDst, node* pSrc)
{ {
CRNLIB_ASSERT(!pDst->state); CRNLIB_ASSERT(!pDst->state);
if (CRNLIB_IS_BITWISE_MOVABLE(Key) && CRNLIB_IS_BITWISE_MOVABLE(Value)) if (CRNLIB_IS_BITWISE_COPYABLE_OR_MOVABLE(Key) && CRNLIB_IS_BITWISE_COPYABLE_OR_MOVABLE(Value))
{ {
memcpy(pDst, pSrc, sizeof(node)); memcpy(pDst, pSrc, sizeof(node));
} }
else else
{ {
if (CRNLIB_IS_BITWISE_MOVABLE(Key)) if (CRNLIB_IS_BITWISE_COPYABLE_OR_MOVABLE(Key))
memcpy(&pDst->first, &pSrc->first, sizeof(Key)); memcpy(&pDst->first, &pSrc->first, sizeof(Key));
else else
{ {
@@ -526,7 +531,7 @@ namespace crnlib
scalar_type<Key>::destruct(&pSrc->first); scalar_type<Key>::destruct(&pSrc->first);
} }
if (CRNLIB_IS_BITWISE_MOVABLE(Value)) if (CRNLIB_IS_BITWISE_COPYABLE_OR_MOVABLE(Value))
memcpy(&pDst->second, &pSrc->second, sizeof(Value)); memcpy(&pDst->second, &pSrc->second, sizeof(Value));
else else
{ {
+156 -45
View File
@@ -4,6 +4,7 @@
#include "crn_color.h" #include "crn_color.h"
#include "crn_vec.h" #include "crn_vec.h"
#include "crn_pixel_format.h" #include "crn_pixel_format.h"
#include "crn_rect.h"
namespace crnlib namespace crnlib
{ {
@@ -25,6 +26,7 @@ namespace crnlib
{ {
} }
// pitch is in PIXELS, not bytes.
image(uint width, uint height, uint pitch = UINT_MAX, const color_type& background = color_type::make_black(), uint flags = pixel_format_helpers::cDefaultCompFlags) : image(uint width, uint height, uint pitch = UINT_MAX, const color_type& background = color_type::make_black(), uint flags = pixel_format_helpers::cDefaultCompFlags) :
m_comp_flags(flags) m_comp_flags(flags)
{ {
@@ -44,6 +46,7 @@ namespace crnlib
set_all(background); set_all(background);
} }
// pitch is in PIXELS, not bytes.
image(color_type* pPixels, uint width, uint height, uint pitch = UINT_MAX, uint flags = pixel_format_helpers::cDefaultCompFlags) image(color_type* pPixels, uint width, uint height, uint pitch = UINT_MAX, uint flags = pixel_format_helpers::cDefaultCompFlags)
{ {
alias(pPixels, width, height, pitch, flags); alias(pPixels, width, height, pitch, flags);
@@ -94,6 +97,7 @@ namespace crnlib
*this = other; *this = other;
} }
// pitch is in PIXELS, not bytes.
void alias(color_type* pPixels, uint width, uint height, uint pitch = UINT_MAX, uint flags = pixel_format_helpers::cDefaultCompFlags) void alias(color_type* pPixels, uint width, uint height, uint pitch = UINT_MAX, uint flags = pixel_format_helpers::cDefaultCompFlags)
{ {
m_pixel_buf.clear(); m_pixel_buf.clear();
@@ -107,6 +111,40 @@ namespace crnlib
m_comp_flags = flags; m_comp_flags = flags;
} }
// pitch is in PIXELS, not bytes.
bool grant_ownership(color_type* pPixels, uint width, uint height, uint pitch = UINT_MAX, uint flags = pixel_format_helpers::cDefaultCompFlags)
{
if (pitch == UINT_MAX)
pitch = width;
if ((!pPixels) || (!width) || (!height) || (pitch < width))
{
CRNLIB_ASSERT(0);
return false;
}
if (pPixels == get_ptr())
{
CRNLIB_ASSERT(0);
return false;
}
clear();
if (!m_pixel_buf.grant_ownership(pPixels, height * pitch, height * pitch))
return false;
m_pPixels = pPixels;
m_width = width;
m_height = height;
m_pitch = pitch;
m_total = pitch * height;
m_comp_flags = flags;
return true;
}
void clear() void clear()
{ {
m_pPixels = NULL; m_pPixels = NULL;
@@ -139,6 +177,34 @@ namespace crnlib
m_pPixels[i] = c; m_pPixels[i] = c;
} }
void flip_x()
{
const uint half_width = m_width / 2;
for (uint y = 0; y < m_height; y++)
{
for (uint x = 0; x < half_width; x++)
{
color_type c((*this)(x, y));
(*this)(x, y) = (*this)(m_width - 1 - x, y);
(*this)(m_width - 1 - x, y) = c;
}
}
}
void flip_y()
{
const uint half_height = m_height / 2;
for (uint y = 0; y < half_height; y++)
{
for (uint x = 0; x < m_width; x++)
{
color_type c((*this)(x, y));
(*this)(x, y) = (*this)(x, m_height - 1 - y);
(*this)(x, m_height - 1 - y) = c;
}
}
}
void convert_to_grayscale() void convert_to_grayscale()
{ {
for (uint y = 0; y < m_height; y++) for (uint y = 0; y < m_height; y++)
@@ -183,13 +249,16 @@ namespace crnlib
bool extract_block(color_type* pDst, uint x, uint y, uint w, uint h, bool flip_xy = false) const bool extract_block(color_type* pDst, uint x, uint y, uint w, uint h, bool flip_xy = false) const
{ {
if ((x >= m_width) || (y >= m_height)) if ((x >= m_width) || (y >= m_height))
{
CRNLIB_ASSERT(0);
return false; return false;
}
if (flip_xy) if (flip_xy)
{ {
for (uint y_ofs = 0; y_ofs < h; y_ofs++) for (uint y_ofs = 0; y_ofs < h; y_ofs++)
for (uint x_ofs = 0; x_ofs < w; x_ofs++) for (uint x_ofs = 0; x_ofs < w; x_ofs++)
pDst[x_ofs * 4 + y_ofs] = get_clamped(x_ofs + x, y_ofs + y); pDst[x_ofs * h + y_ofs] = get_clamped(x_ofs + x, y_ofs + y); // 5/4/12 - this was incorrectly x_ofs * 4
} }
else if (((x + w) > m_width) || ((y + h) > m_height)) else if (((x + w) > m_width) || ((y + h) > m_height))
{ {
@@ -213,10 +282,14 @@ namespace crnlib
return true; return true;
} }
void fill(uint x, uint y, uint w, uint h, const color_type& c) // No clipping!
void unclipped_fill_box(uint x, uint y, uint w, uint h, const color_type& c)
{ {
CRNLIB_ASSERT((x + w) <= m_width); if (((x + w) > m_width) || ((y + h) > m_height))
CRNLIB_ASSERT((y + h) <= m_height); {
CRNLIB_ASSERT(0);
return;
}
color_type* p = get_scanline(y) + x; color_type* p = get_scanline(y) + x;
@@ -229,7 +302,7 @@ namespace crnlib
} }
} }
void draw_box(int x, int y, uint width, uint height, const color_type& c) void draw_rect(int x, int y, uint width, uint height, const color_type& c)
{ {
draw_line(x, y, x + width - 1, y, c); draw_line(x, y, x + width - 1, y, c);
draw_line(x, y, x, y + height - 1, c); draw_line(x, y, x, y + height - 1, c);
@@ -238,13 +311,25 @@ namespace crnlib
} }
// No clipping! // No clipping!
bool copy(uint src_x, uint src_y, uint src_w, uint src_h, uint dst_x, uint dst_y, const image& src) bool unclipped_blit(uint src_x, uint src_y, uint src_w, uint src_h, uint dst_x, uint dst_y, const image& src)
{ {
if ( ((src_x + src_w) > src.get_width()) || ((src_y + src_h) > src.get_height()) ) if ((!is_valid()) || (!src.is_valid()))
{
CRNLIB_ASSERT(0);
return false; return false;
}
if ( ((src_x + src_w) > src.get_width()) || ((src_y + src_h) > src.get_height()) )
{
CRNLIB_ASSERT(0);
return false;
}
if ( ((dst_x + src_w) > get_width()) || ((dst_y + src_h) > get_height()) ) if ( ((dst_x + src_w) > get_width()) || ((dst_y + src_h) > get_height()) )
{
CRNLIB_ASSERT(0);
return false; return false;
}
const color_type* pS = &src(src_x, src_y); const color_type* pS = &src(src_x, src_y);
color_type* pD = &(*this)(dst_x, dst_y); color_type* pD = &(*this)(dst_x, dst_y);
@@ -262,38 +347,74 @@ namespace crnlib
} }
// With clipping. // With clipping.
void blit(int dst_x, int dst_y, const image& src) bool blit(int dst_x, int dst_y, const image& src)
{ {
uint src_x = 0; if ((!is_valid()) || (!src.is_valid()))
uint src_y = 0; {
CRNLIB_ASSERT(0);
return false;
}
int src_x = 0;
int src_y = 0;
if (dst_x < 0) if (dst_x < 0)
{ {
src_x = -dst_x; src_x = -dst_x;
if (src_x >= src.get_width()) if (src_x >= static_cast<int>(src.get_width()))
return; return false;
dst_x = 0; dst_x = 0;
} }
if (dst_y < 0) if (dst_y < 0)
{ {
src_y = -dst_y; src_y = -dst_y;
if (src_y >= src.get_height()) if (src_y >= static_cast<int>(src.get_height()))
return; return false;
dst_y = 0; dst_y = 0;
} }
if ((dst_x >= (int)m_width) || (dst_y >= (int)m_height)) if ((dst_x >= (int)m_width) || (dst_y >= (int)m_height))
return; return false;
uint width = math::minimum(m_width - dst_x, src.get_width() - src_x); uint width = math::minimum(m_width - dst_x, src.get_width() - src_x);
uint height = math::minimum(m_height - dst_y, src.get_height() - src_y); uint height = math::minimum(m_height - dst_y, src.get_height() - src_y);
bool success = copy(src_x, src_y, width, height, dst_x, dst_y, src); bool success = unclipped_blit(src_x, src_y, width, height, dst_x, dst_y, src);
success; success;
CRNLIB_ASSERT(success); CRNLIB_ASSERT(success);
return true;
} }
// With clipping.
bool blit(int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, const image& src)
{
if ((!is_valid()) || (!src.is_valid()))
{
CRNLIB_ASSERT(0);
return false;
}
rect src_rect(src_x, src_y, src_x + src_w, src_y + src_h);
if (!src_rect.intersect(src.get_bounds()))
return false;
rect dst_rect(dst_x, dst_y, dst_x + src_rect.get_width(), dst_y + src_rect.get_height());
if (!dst_rect.intersect(get_bounds()))
return false;
bool success = unclipped_blit(
src_rect.get_left(), src_rect.get_top(),
math::minimum(src_rect.get_width(), dst_rect.get_width()), math::minimum(src_rect.get_height(), dst_rect.get_height()),
dst_rect.get_left(), dst_rect.get_top(), src);
success;
CRNLIB_ASSERT(success);
return true;
}
// In-place resize of image dimensions (cropping).
bool resize(uint new_width, uint new_height, uint new_pitch = UINT_MAX, const color_type background = color_type::make_black()) bool resize(uint new_width, uint new_height, uint new_pitch = UINT_MAX, const color_type background = color_type::make_black())
{ {
if (new_pitch == UINT_MAX) if (new_pitch == UINT_MAX)
@@ -341,6 +462,8 @@ namespace crnlib
inline uint get_height() const { return m_height; } inline uint get_height() const { return m_height; }
inline uint get_total_pixels() const { return m_width * m_height; } inline uint get_total_pixels() const { return m_width * m_height; }
inline rect get_bounds() const { return rect(0, 0, m_width, m_height); }
inline uint get_pitch() const { return m_pitch; } inline uint get_pitch() const { return m_pitch; }
inline uint get_pitch_in_bytes() const { return m_pitch * sizeof(color_type); } inline uint get_pitch_in_bytes() const { return m_pitch * sizeof(color_type); }
@@ -368,15 +491,21 @@ namespace crnlib
return m_pPixels[x + y * m_pitch]; return m_pPixels[x + y * m_pitch];
} }
inline const color_type& get_clamped (int x, int y) const inline const color_type& get_unclamped(uint x, uint y) const
{
CRNLIB_ASSERT((x < m_width) && (y < m_height));
return m_pPixels[x + y * m_pitch];
}
inline const color_type& get_clamped(int x, int y) const
{ {
x = math::clamp<int>(x, 0, m_width - 1); x = math::clamp<int>(x, 0, m_width - 1);
y = math::clamp<int>(y, 0, m_height - 1); y = math::clamp<int>(y, 0, m_height - 1);
return (*this)((uint)x, (uint)y); return m_pPixels[x + y * m_pitch];
} }
// Sample image with bilinear filtering. // Sample image with bilinear filtering.
// (x,y) - Continuous coordinates, where pixel centers are at (.5,.5), valid image coords are (0,width] and (0,height]. // (x,y) - Continuous coordinates, where pixel centers are at (.5,.5), valid image coords are [0,width] and [0,height].
void get_filtered(float x, float y, color_type& result) const void get_filtered(float x, float y, color_type& result) const
{ {
x -= .5f; x -= .5f;
@@ -430,7 +559,7 @@ namespace crnlib
} }
} }
inline void set_pixel(uint x, uint y, const color_type& c) inline void set_pixel_unclipped(uint x, uint y, const color_type& c)
{ {
CRNLIB_ASSERT((x < m_width) && (y < m_height)); CRNLIB_ASSERT((x < m_width) && (y < m_height));
m_pPixels[x + y * m_pitch] = c; m_pPixels[x + y * m_pitch] = c;
@@ -438,7 +567,7 @@ namespace crnlib
inline void set_pixel_clipped(int x, int y, const color_type& c) inline void set_pixel_clipped(int x, int y, const color_type& c)
{ {
if ((x < 0) || (x >= (int)m_width) || (y < 0) || (y >= (int)m_height)) if ((static_cast<uint>(x) >= m_width) || (static_cast<uint>(y) >= m_height))
return; return;
m_pPixels[x + y * m_pitch] = c; m_pPixels[x + y * m_pitch] = c;
@@ -486,7 +615,6 @@ namespace crnlib
} }
int dx = xe - xs, dy = ye - ys; int dx = xe - xs, dy = ye - ys;
if (!dx) if (!dx)
{ {
if (ys > ye) if (ys > ye)
@@ -503,35 +631,26 @@ namespace crnlib
{ {
if (dy <= dx) if (dy <= dx)
{ {
int e = 2 * dy - dx; int e = 2 * dy - dx, e_no_inc = 2 * dy, e_inc = 2 * (dy - dx);
int e_no_inc = 2 * dy;
int e_inc = 2 * (dy - dx);
rasterize_line(xs, ys, xe, ye, 0, 1, e, e_inc, e_no_inc, color); rasterize_line(xs, ys, xe, ye, 0, 1, e, e_inc, e_no_inc, color);
} }
else else
{ {
int e = 2 * dx - dy; int e = 2 * dx - dy, e_no_inc = 2 * dx, e_inc = 2 * (dx - dy);
int e_no_inc = 2 * dx;
int e_inc = 2 * (dx - dy);
rasterize_line(xs, ys, xe, ye, 1, 1, e, e_inc, e_no_inc, color); rasterize_line(xs, ys, xe, ye, 1, 1, e, e_inc, e_no_inc, color);
} }
} }
else else
{ {
dy = -dy; dy = -dy;
if (dy <= dx) if (dy <= dx)
{ {
int e = 2 * dy - dx; int e = 2 * dy - dx, e_no_inc = 2 * dy, e_inc = 2 * (dy - dx);
int e_no_inc = 2 * dy;
int e_inc = 2 * (dy - dx);
rasterize_line(xs, ys, xe, ye, 0, -1, e, e_inc, e_no_inc, color); rasterize_line(xs, ys, xe, ye, 0, -1, e, e_inc, e_no_inc, color);
} }
else else
{ {
int e = 2 * dx - dy; int e = 2 * dx - dy, e_no_inc = (2 * dx), e_inc = 2 * (dx - dy);
int e_no_inc = (2 * dx);
int e_inc = 2 * (dx - dy);
rasterize_line(xe, ye, xs, ys, 1, -1, e, e_inc, e_no_inc, color); rasterize_line(xe, ye, xs, ys, 1, -1, e, e_inc, e_no_inc, color);
} }
} }
@@ -557,14 +676,10 @@ namespace crnlib
if (pred) if (pred)
{ {
start = ys; start = ys; end = ye; var = xs;
end = ye;
var = xs;
for (int i = start; i <= end; i++) for (int i = start; i <= end; i++)
{ {
set_pixel_clipped(var, i, color); set_pixel_clipped(var, i, color);
if (e < 0) if (e < 0)
e += e_no_inc; e += e_no_inc;
else else
@@ -576,14 +691,10 @@ namespace crnlib
} }
else else
{ {
start = xs; start = xs; end = xe; var = ys;
end = xe;
var = ys;
for (int i = start; i <= end; i++) for (int i = start; i <= end; i++)
{ {
set_pixel_clipped(i, var, color); set_pixel_clipped(i, var, color);
if (e < 0) if (e < 0)
e += e_no_inc; e += e_no_inc;
else else
+238 -44
View File
@@ -8,27 +8,39 @@
#include "crn_strutils.h" #include "crn_strutils.h"
#include "crn_file_utils.h" #include "crn_file_utils.h"
#include "crn_threading.h" #include "crn_threading.h"
#include "crn_miniz.h"
#include "crn_jpge.h"
#include "crn_cfile_stream.h"
#include "crn_mipmapped_texture.h"
#include "crn_buffer_stream.h"
#define STBI_HEADER_FILE_ONLY #define STBI_HEADER_FILE_ONLY
#include "crn_stb_image.cpp" #include "crn_stb_image.cpp"
#include "crn_jpgd.h"
#include "crn_pixel_format.h" #include "crn_pixel_format.h"
namespace crnlib namespace crnlib
{ {
const float cInfinitePSNR = 999999.0f; const float cInfinitePSNR = 999999.0f;
const uint CRNLIB_LARGEST_SUPPORTED_IMAGE_DIMENSION = 16384;
namespace image_utils namespace image_utils
{ {
bool load_from_file_stb(const char* pFilename, image_u8& img) bool read_from_stream_stb(data_stream_serializer &serializer, image_u8& img)
{ {
uint8_vec buf;
if (!serializer.read_entire_file(buf))
return false;
int x = 0, y = 0, n = 0; int x = 0, y = 0, n = 0;
unsigned char* pData = stbi_load(pFilename, &x, &y, &n, 4); unsigned char* pData = stbi_load_from_memory(buf.get_ptr(), buf.size_in_bytes(), &x, &y, &n, 4);
if (!pData) if (!pData)
return false; return false;
if ((x > 8192) || (y > 8192)) if ((x > (int)CRNLIB_LARGEST_SUPPORTED_IMAGE_DIMENSION) || (y > (int)CRNLIB_LARGEST_SUPPORTED_IMAGE_DIMENSION))
{ {
stbi_image_free(pData); stbi_image_free(pData);
return false; return false;
@@ -68,30 +80,123 @@ namespace crnlib
return true; return true;
} }
bool save_to_file_stb(const char* pFilename, const image_u8& img, uint save_flags, int comp_index) bool read_from_stream_jpgd(data_stream_serializer &serializer, image_u8& img)
{ {
if (!img.get_width()) uint8_vec buf;
if (!serializer.read_entire_file(buf))
return false; return false;
bool bSaveBMP = false; int width = 0, height = 0, actual_comps = 0;
unsigned char *pSrc_img = jpgd::decompress_jpeg_image_from_memory(buf.get_ptr(), buf.size_in_bytes(), &width, &height, &actual_comps, 4);
if (!pSrc_img)
return false;
if (math::maximum(width, height) > (int)CRNLIB_LARGEST_SUPPORTED_IMAGE_DIMENSION)
{
crnlib_free(pSrc_img);
return false;
}
if (!img.grant_ownership(reinterpret_cast<color_quad_u8*>(pSrc_img), width, height))
{
crnlib_free(pSrc_img);
return false;
}
img.reset_comp_flags();
img.set_grayscale(actual_comps == 1);
img.set_component_valid(3, false);
return true;
}
bool read_from_stream(image_u8& dest, data_stream_serializer& serializer, uint read_flags)
{
if (read_flags > cReadFlagsAllFlags)
{
CRNLIB_ASSERT(0);
return false;
}
if (!serializer.get_stream())
{
CRNLIB_ASSERT(0);
return false;
}
dynamic_string ext(serializer.get_name());
file_utils::get_extension(ext);
if ((ext == "jpg") || (ext == "jpeg"))
{
// Use my jpeg decoder by default because it supports progressive jpeg's.
if ((read_flags & cReadFlagForceSTB) == 0)
{
return image_utils::read_from_stream_jpgd(serializer, dest);
}
}
return image_utils::read_from_stream_stb(serializer, dest);
}
bool read_from_file(image_u8& dest, const char* pFilename, uint read_flags)
{
if (read_flags > cReadFlagsAllFlags)
{
CRNLIB_ASSERT(0);
return false;
}
cfile_stream file_stream;
if (!file_stream.open(pFilename))
return false;
data_stream_serializer serializer(file_stream);
return read_from_stream(dest, serializer, read_flags);
}
bool write_to_file(const char* pFilename, const image_u8& img, uint write_flags, int grayscale_comp_index)
{
if ((grayscale_comp_index < -1) || (grayscale_comp_index > 3))
{
CRNLIB_ASSERT(0);
return false;
}
if (!img.get_width())
{
CRNLIB_ASSERT(0);
return false;
}
dynamic_string ext(pFilename); dynamic_string ext(pFilename);
bool is_jpeg = false;
if (file_utils::get_extension(ext)) if (file_utils::get_extension(ext))
{ {
if (ext == "bmp") is_jpeg = ((ext == "jpg") || (ext == "jpeg"));
bSaveBMP = true;
else if (ext != "tga") if ((ext != "png") && (ext != "bmp") && (ext != "tga") && (!is_jpeg))
{ {
console::error("crnlib::image_utils::save_to_file_stb: Can only write .BMP or .TGA files!\n"); console::error("crnlib::image_utils::write_to_file: Can only write .BMP, .TGA, .PNG, or .JPG files!\n");
return false; return false;
} }
} }
if ((img.get_comp_flags() & pixel_format_helpers::cCompFlagGrayscale) || (save_flags & image_utils::cSaveGrayscale)) crnlib::vector<uint8> temp;
{ uint num_src_chans = 0;
CRNLIB_ASSERT(comp_index < 4); const void *pSrc_img = NULL;
if (comp_index > 3) comp_index = 3;
crnlib::vector<uint8> temp(img.get_total_pixels()); if (is_jpeg)
{
write_flags |= cWriteFlagIgnoreAlpha;
}
if ((img.get_comp_flags() & pixel_format_helpers::cCompFlagGrayscale) || (write_flags & image_utils::cWriteFlagGrayscale))
{
CRNLIB_ASSERT(grayscale_comp_index < 4);
if (grayscale_comp_index > 3) grayscale_comp_index = 3;
temp.resize(img.get_total_pixels());
for (uint y = 0; y < img.get_height(); y++) for (uint y = 0; y < img.get_height(); y++)
{ {
@@ -104,7 +209,7 @@ namespace crnlib
while (pSrc != pSrc_end) while (pSrc != pSrc_end)
*pDst++ = (*pSrc++)[1]; *pDst++ = (*pSrc++)[1];
} }
else if (comp_index < 0) else if (grayscale_comp_index < 0)
{ {
while (pSrc != pSrc_end) while (pSrc != pSrc_end)
*pDst++ = static_cast<uint8>((*pSrc++).get_luma()); *pDst++ = static_cast<uint8>((*pSrc++).get_luma());
@@ -112,15 +217,16 @@ namespace crnlib
else else
{ {
while (pSrc != pSrc_end) while (pSrc != pSrc_end)
*pDst++ = (*pSrc++)[comp_index]; *pDst++ = (*pSrc++)[grayscale_comp_index];
} }
} }
return (bSaveBMP ? stbi_write_bmp : stbi_write_tga)(pFilename, img.get_width(), img.get_height(), 1, &temp[0]) == CRNLIB_TRUE; pSrc_img = &temp[0];
num_src_chans = 1;
} }
else if ((!img.is_component_valid(3)) || (save_flags & cSaveIgnoreAlpha)) else if ((!img.is_component_valid(3)) || (write_flags & cWriteFlagIgnoreAlpha))
{ {
crnlib::vector<uint8> temp(img.get_total_pixels() * 3); temp.resize(img.get_total_pixels() * 3);
for (uint y = 0; y < img.get_height(); y++) for (uint y = 0; y < img.get_height(); y++)
{ {
@@ -140,37 +246,47 @@ namespace crnlib
} }
} }
return (bSaveBMP ? stbi_write_bmp : stbi_write_tga)(pFilename, img.get_width(), img.get_height(), 3, &temp[0]) == CRNLIB_TRUE; num_src_chans = 3;
pSrc_img = &temp[0];
} }
else else
{ {
return (bSaveBMP ? stbi_write_bmp : stbi_write_tga)(pFilename, img.get_width(), img.get_height(), 4, img.get_ptr()) == CRNLIB_TRUE; num_src_chans = 4;
pSrc_img = img.get_ptr();
} }
}
bool load_from_file(image_u8& dest, const char* pFilename, int flags) bool success = false;
{ if (ext == "png")
flags; {
return image_utils::load_from_file_stb(pFilename, dest); size_t png_image_size = 0;
} void *pPNG_image_data = tdefl_write_image_to_png_file_in_memory(pSrc_img, img.get_width(), img.get_height(), num_src_chans, &png_image_size);
if (!pPNG_image_data)
return false;
success = file_utils::write_buf_to_file(pFilename, pPNG_image_data, png_image_size);
mz_free(pPNG_image_data);
}
else if (is_jpeg)
{
jpge::params params;
if (write_flags & cWriteFlagJPEGQualityLevelMask)
params.m_quality = math::clamp<uint>((write_flags & cWriteFlagJPEGQualityLevelMask) >> cWriteFlagJPEGQualityLevelShift, 1U, 100U);
params.m_two_pass_flag = (write_flags & cWriteFlagJPEGTwoPass) != 0;
params.m_no_chroma_discrim_flag = (write_flags & cWriteFlagJPEGNoChromaDiscrim) != 0;
bool save_to_grayscale_file(const char* pFilename, const image_u8& src, int component, int flags) if (write_flags & cWriteFlagJPEGH1V1)
{ params.m_subsampling = jpge::H1V1;
flags; else if (write_flags & cWriteFlagJPEGH2V1)
return image_utils::save_to_file_stb(pFilename, src, image_utils::cSaveGrayscale, component); params.m_subsampling = jpge::H2V1;
} else if (write_flags & cWriteFlagJPEGH2V2)
params.m_subsampling = jpge::H2V2;
bool save_to_file(const char* pFilename, const image_u8& src, int flags, bool ignore_alpha) success = jpge::compress_image_to_jpeg_file(pFilename, img.get_width(), img.get_height(), num_src_chans, (const jpge::uint8*)pSrc_img, params);
{ }
if (src.is_grayscale())
return save_to_grayscale_file(pFilename, src, cSaveLuma, flags);
else else
{ {
uint save_flags = 0; success = ((ext == "bmp" ? stbi_write_bmp : stbi_write_tga)(pFilename, img.get_width(), img.get_height(), num_src_chans, pSrc_img) == CRNLIB_TRUE);
if (ignore_alpha)
save_flags |= image_utils::cSaveIgnoreAlpha;
return image_utils::save_to_file_stb(pFilename, src, save_flags);
} }
return success;
} }
bool has_alpha(const image_u8& img) bool has_alpha(const image_u8& img)
@@ -714,7 +830,7 @@ namespace crnlib
if (!total_blocks) if (!total_blocks)
return 0.0f; return 0.0f;
//save_to_file_stb("ssim.tga", yimg, cSaveGrayscale); //save_to_file_stb_or_miniz("ssim.tga", yimg, cWriteFlagGrayscale);
return total_ssim / total_blocks; return total_ssim / total_blocks;
} }
@@ -739,9 +855,9 @@ namespace crnlib
void error_metrics::print(const char* pName) const void error_metrics::print(const char* pName) const
{ {
if (mPeakSNR >= cInfinitePSNR) if (mPeakSNR >= cInfinitePSNR)
console::printf("%s Error: Max: %3u, Mean: %3.3f, MSE: %3.3f, RMS: %3.3f, PSNR: Infinite", pName, mMax, mMean, mMeanSquared, mRootMeanSquared); console::printf("%s Error: Max: %3u, Mean: %3.3f, MSE: %3.3f, RMSE: %3.3f, PSNR: Infinite", pName, mMax, mMean, mMeanSquared, mRootMeanSquared);
else else
console::printf("%s Error: Max: %3u, Mean: %3.3f, MSE: %3.3f, RMS: %3.3f, PSNR: %3.3f", pName, mMax, mMean, mMeanSquared, mRootMeanSquared, mPeakSNR); console::printf("%s Error: Max: %3u, Mean: %3.3f, MSE: %3.3f, RMSE: %3.3f, PSNR: %3.3f", pName, mMax, mMean, mMeanSquared, mRootMeanSquared, mPeakSNR);
} }
bool error_metrics::compute(const image_u8& a, const image_u8& b, uint first_channel, uint num_channels, bool average_component_error) bool error_metrics::compute(const image_u8& a, const image_u8& b, uint first_channel, uint num_channels, bool average_component_error)
@@ -926,6 +1042,11 @@ namespace crnlib
img.set_comp_flags(static_cast<pixel_format_helpers::component_flags>(pixel_format_helpers::cCompFlagRValid | pixel_format_helpers::cCompFlagGValid | pixel_format_helpers::cCompFlagBValid | pixel_format_helpers::cCompFlagGrayscale | (img.has_alpha() ? pixel_format_helpers::cCompFlagAValid : 0))); img.set_comp_flags(static_cast<pixel_format_helpers::component_flags>(pixel_format_helpers::cCompFlagRValid | pixel_format_helpers::cCompFlagGValid | pixel_format_helpers::cCompFlagBValid | pixel_format_helpers::cCompFlagGrayscale | (img.has_alpha() ? pixel_format_helpers::cCompFlagAValid : 0)));
break; break;
} }
case cConversion_To_Y:
{
img.set_comp_flags(static_cast<pixel_format_helpers::component_flags>(img.get_comp_flags() | pixel_format_helpers::cCompFlagGrayscale));
break;
}
default: default:
{ {
CRNLIB_ASSERT(false); CRNLIB_ASSERT(false);
@@ -1035,6 +1156,15 @@ namespace crnlib
dst.a = src.a; dst.a = src.a;
break; break;
} }
case image_utils::cConversion_To_Y:
{
uint8 y = static_cast<uint8>(src.get_luma());
dst.r = y;
dst.g = y;
dst.b = y;
dst.a = src.a;
break;
}
default: default:
{ {
CRNLIB_ASSERT(false); CRNLIB_ASSERT(false);
@@ -1168,6 +1298,70 @@ namespace crnlib
return sqrt(var); return sqrt(var);
} }
uint8* read_image_from_memory(const uint8* pImage, int nSize, int* pWidth, int* pHeight, int* pActualComps, int req_comps, const char* pFilename)
{
*pWidth = 0;
*pHeight = 0;
*pActualComps = 0;
if ((req_comps < 1) || (req_comps > 4))
return false;
mipmapped_texture tex;
buffer_stream buf_stream(pImage, nSize);
buf_stream.set_name(pFilename);
data_stream_serializer serializer(buf_stream);
if (!tex.read_from_stream(serializer))
return NULL;
if (tex.is_packed())
{
if (!tex.unpack_from_dxt(true))
return NULL;
}
image_u8 img;
image_u8* pImg = tex.get_level_image(0, 0, img);
if (!pImg)
return NULL;
*pWidth = tex.get_width();
*pHeight = tex.get_height();
if (pImg->has_alpha())
*pActualComps = 4;
else if (pImg->is_grayscale())
*pActualComps = 1;
else
*pActualComps = 3;
uint8 *pDst = NULL;
if (req_comps == 4)
{
pDst = (uint8*)malloc(tex.get_total_pixels() * sizeof(uint32));
uint8 *pSrc = (uint8*)pImg->get_ptr();
memcpy(pDst, pSrc, tex.get_total_pixels() * sizeof(uint32));
}
else
{
image_u8 luma_img;
if (req_comps == 1)
{
luma_img = *pImg;
luma_img.convert_to_grayscale();
pImg = &luma_img;
}
pixel_packer packer(req_comps, 8);
uint32 n;
pDst = image_utils::pack_image(*pImg, packer, n);
}
return pDst;
}
} // namespace image_utils } // namespace image_utils
} // namespace crnlib } // namespace crnlib
+64 -12
View File
@@ -2,6 +2,7 @@
// See Copyright Notice and license at the end of inc/crnlib.h // See Copyright Notice and license at the end of inc/crnlib.h
#pragma once #pragma once
#include "crn_image.h" #include "crn_image.h"
#include "crn_data_stream_serializer.h"
namespace crnlib namespace crnlib
{ {
@@ -9,23 +10,41 @@ namespace crnlib
namespace image_utils namespace image_utils
{ {
bool load_from_file_stb(const char* pFilename, image_u8& img); enum read_flags_t
{
cReadFlagForceSTB = 1,
cReadFlagsAllFlags = 1
};
bool read_from_stream_stb(data_stream_serializer& serializer, image_u8& img);
bool read_from_stream_jpgd(data_stream_serializer& serializer, image_u8& img);
bool read_from_stream(image_u8& dest, data_stream_serializer& serializer, uint read_flags = 0);
bool read_from_file(image_u8& dest, const char* pFilename, uint read_flags = 0);
// Reads texture from memory, results returned stb_image.c style.
// *pActual_comps is set to 1, 3, or 4. req_comps must range from 1-4.
uint8* read_from_memory(const uint8* pImage, int nSize, int* pWidth, int* pHeight, int* pActualComps, int req_comps, const char* pFilename);
enum enum
{ {
cSaveIgnoreAlpha = 1, cWriteFlagIgnoreAlpha = 0x00000001,
cSaveGrayscale = 2 cWriteFlagGrayscale = 0x00000002,
cWriteFlagJPEGH1V1 = 0x00010000,
cWriteFlagJPEGH2V1 = 0x00020000,
cWriteFlagJPEGH2V2 = 0x00040000,
cWriteFlagJPEGTwoPass = 0x00080000,
cWriteFlagJPEGNoChromaDiscrim = 0x00100000,
cWriteFlagJPEGQualityLevelMask = 0xFF000000,
cWriteFlagJPEGQualityLevelShift = 24,
}; };
const int cSaveLuma = -1; const int cLumaComponentIndex = -1;
bool save_to_file_stb(const char* pFilename, const image_u8& img, uint save_flags = 0, int comp_index = cSaveLuma); inline uint create_jpeg_write_flags(uint base_flags, uint quality_level) { CRNLIB_ASSERT(quality_level <= 100); return base_flags | ((quality_level << cWriteFlagJPEGQualityLevelShift) & cWriteFlagJPEGQualityLevelMask); }
bool load_from_file(image_u8& dest, const char* pFilename, int flags = 0); bool write_to_file(const char* pFilename, const image_u8& img, uint write_flags = 0, int grayscale_comp_index = cLumaComponentIndex);
bool save_to_grayscale_file(const char* pFilename, const image_u8& src, int component, int flags = 0);
bool save_to_file(const char* pFilename, const image_u8& src, int flags = 0, bool ignore_alpha = false);
bool has_alpha(const image_u8& img); bool has_alpha(const image_u8& img);
bool is_normal_map(const image_u8& img, const char* pFilename = NULL); bool is_normal_map(const image_u8& img, const char* pFilename = NULL);
@@ -127,15 +146,48 @@ namespace crnlib
cConversion_A_To_RGBA, cConversion_A_To_RGBA,
cConversion_Y_To_RGB, cConversion_Y_To_RGB,
cConversion_To_Y,
cConversionTotal cConversionTotal
}; };
void convert_image(image_u8& img, conversion_type conv_type); void convert_image(image_u8& img, conversion_type conv_type);
template<typename image_type>
inline uint8* pack_image(const image_type& img, const pixel_packer& packer, uint& n)
{
n = 0;
if (!packer.is_valid())
return NULL;
const uint width = img.get_width(), height = img.get_height();
uint dst_pixel_stride = packer.get_pixel_stride();
uint dst_pitch = width * dst_pixel_stride;
n = dst_pitch * height;
uint8* pImage = static_cast<uint8*>(crnlib_malloc(n));
uint8* pDst = pImage;
for (uint y = 0; y < height; y++)
{
const typename image_type::color_t* pSrc = img.get_scanline(y);
for (uint x = 0; x < width; x++)
pDst = (uint8*)packer.pack(*pSrc++, pDst);
}
return pImage;
}
image_utils::conversion_type get_conversion_type(bool cooking, pixel_format fmt); image_utils::conversion_type get_conversion_type(bool cooking, pixel_format fmt);
image_utils::conversion_type get_image_conversion_type_from_crn_format(crn_format fmt); image_utils::conversion_type get_image_conversion_type_from_crn_format(crn_format fmt);
double compute_std_dev(uint n, const color_quad_u8* pPixels, uint first_channel, uint num_channels); double compute_std_dev(uint n, const color_quad_u8* pPixels, uint first_channel, uint num_channels);
}
} uint8* read_image_from_memory(const uint8* pImage, int nSize, int* pWidth, int* pHeight, int* pActualComps, int req_comps, const char* pFilename);
} // namespace image_utils
} // namespace crnlib
+3172
View File
File diff suppressed because it is too large Load Diff
+319
View File
@@ -0,0 +1,319 @@
// jpgd.h - C++ class for JPEG decompression.
// Public domain, Rich Geldreich <richgel99@gmail.com>
#ifndef JPEG_DECODER_H
#define JPEG_DECODER_H
#include <stdlib.h>
#include <stdio.h>
#include <setjmp.h>
#ifdef _MSC_VER
#define JPGD_NORETURN __declspec(noreturn)
#elif defined(__GNUC__)
#define JPGD_NORETURN __attribute__ ((noreturn))
#else
#define JPGD_NORETURN
#endif
namespace jpgd
{
typedef unsigned char uint8;
typedef signed short int16;
typedef unsigned short uint16;
typedef unsigned int uint;
typedef signed int int32;
// Loads a JPEG image from a memory buffer or a file.
// req_comps can be 1 (grayscale), 3 (RGB), or 4 (RGBA).
// On return, width/height will be set to the image's dimensions, and actual_comps will be set to the either 1 (grayscale) or 3 (RGB).
// Notes: For more control over where and how the source data is read, see the decompress_jpeg_image_from_stream() function below, or call the jpeg_decoder class directly.
// Requesting a 8 or 32bpp image is currently a little faster than 24bpp because the jpeg_decoder class itself currently always unpacks to either 8 or 32bpp.
unsigned char *decompress_jpeg_image_from_memory(const unsigned char *pSrc_data, int src_data_size, int *width, int *height, int *actual_comps, int req_comps);
unsigned char *decompress_jpeg_image_from_file(const char *pSrc_filename, int *width, int *height, int *actual_comps, int req_comps);
// Success/failure error codes.
enum jpgd_status
{
JPGD_SUCCESS = 0, JPGD_FAILED = -1, JPGD_DONE = 1,
JPGD_BAD_DHT_COUNTS = -256, JPGD_BAD_DHT_INDEX, JPGD_BAD_DHT_MARKER, JPGD_BAD_DQT_MARKER, JPGD_BAD_DQT_TABLE,
JPGD_BAD_PRECISION, JPGD_BAD_HEIGHT, JPGD_BAD_WIDTH, JPGD_TOO_MANY_COMPONENTS,
JPGD_BAD_SOF_LENGTH, JPGD_BAD_VARIABLE_MARKER, JPGD_BAD_DRI_LENGTH, JPGD_BAD_SOS_LENGTH,
JPGD_BAD_SOS_COMP_ID, JPGD_W_EXTRA_BYTES_BEFORE_MARKER, JPGD_NO_ARITHMITIC_SUPPORT, JPGD_UNEXPECTED_MARKER,
JPGD_NOT_JPEG, JPGD_UNSUPPORTED_MARKER, JPGD_BAD_DQT_LENGTH, JPGD_TOO_MANY_BLOCKS,
JPGD_UNDEFINED_QUANT_TABLE, JPGD_UNDEFINED_HUFF_TABLE, JPGD_NOT_SINGLE_SCAN, JPGD_UNSUPPORTED_COLORSPACE,
JPGD_UNSUPPORTED_SAMP_FACTORS, JPGD_DECODE_ERROR, JPGD_BAD_RESTART_MARKER, JPGD_ASSERTION_ERROR,
JPGD_BAD_SOS_SPECTRAL, JPGD_BAD_SOS_SUCCESSIVE, JPGD_STREAM_READ, JPGD_NOTENOUGHMEM
};
// Input stream interface.
// Derive from this class to read input data from sources other than files or memory. Set m_eof_flag to true when no more data is available.
// The decoder is rather greedy: it will keep on calling this method until its internal input buffer is full, or until the EOF flag is set.
// It the input stream contains data after the JPEG stream's EOI (end of image) marker it will probably be pulled into the internal buffer.
// Call the get_total_bytes_read() method to determine the actual size of the JPEG stream after successful decoding.
class jpeg_decoder_stream
{
public:
jpeg_decoder_stream() { }
virtual ~jpeg_decoder_stream() { }
// The read() method is called when the internal input buffer is empty.
// Parameters:
// pBuf - input buffer
// max_bytes_to_read - maximum bytes that can be written to pBuf
// pEOF_flag - set this to true if at end of stream (no more bytes remaining)
// Returns -1 on error, otherwise return the number of bytes actually written to the buffer (which may be 0).
// Notes: This method will be called in a loop until you set *pEOF_flag to true or the internal buffer is full.
virtual int read(uint8 *pBuf, int max_bytes_to_read, bool *pEOF_flag) = 0;
};
// stdio FILE stream class.
class jpeg_decoder_file_stream : public jpeg_decoder_stream
{
jpeg_decoder_file_stream(const jpeg_decoder_file_stream &);
jpeg_decoder_file_stream &operator =(const jpeg_decoder_file_stream &);
FILE *m_pFile;
bool m_eof_flag, m_error_flag;
public:
jpeg_decoder_file_stream();
virtual ~jpeg_decoder_file_stream();
bool open(const char *Pfilename);
void close();
virtual int read(uint8 *pBuf, int max_bytes_to_read, bool *pEOF_flag);
};
// Memory stream class.
class jpeg_decoder_mem_stream : public jpeg_decoder_stream
{
const uint8 *m_pSrc_data;
uint m_ofs, m_size;
public:
jpeg_decoder_mem_stream() : m_pSrc_data(NULL), m_ofs(0), m_size(0) { }
jpeg_decoder_mem_stream(const uint8 *pSrc_data, uint size) : m_pSrc_data(pSrc_data), m_ofs(0), m_size(size) { }
virtual ~jpeg_decoder_mem_stream() { }
bool open(const uint8 *pSrc_data, uint size);
void close() { m_pSrc_data = NULL; m_ofs = 0; m_size = 0; }
virtual int read(uint8 *pBuf, int max_bytes_to_read, bool *pEOF_flag);
};
// Loads JPEG file from a jpeg_decoder_stream.
unsigned char *decompress_jpeg_image_from_stream(jpeg_decoder_stream *pStream, int *width, int *height, int *actual_comps, int req_comps);
enum
{
JPGD_IN_BUF_SIZE = 8192, JPGD_MAX_BLOCKS_PER_MCU = 10, JPGD_MAX_HUFF_TABLES = 8, JPGD_MAX_QUANT_TABLES = 4,
JPGD_MAX_COMPONENTS = 4, JPGD_MAX_COMPS_IN_SCAN = 4, JPGD_MAX_BLOCKS_PER_ROW = 8192, JPGD_MAX_HEIGHT = 16384, JPGD_MAX_WIDTH = 16384
};
typedef int16 jpgd_quant_t;
typedef int16 jpgd_block_t;
class jpeg_decoder
{
public:
// Call get_error_code() after constructing to determine if the stream is valid or not. You may call the get_width(), get_height(), etc.
// methods after the constructor is called. You may then either destruct the object, or begin decoding the image by calling begin_decoding(), then decode() on each scanline.
jpeg_decoder(jpeg_decoder_stream *pStream);
~jpeg_decoder();
// Call this method after constructing the object to begin decompression.
// If JPGD_SUCCESS is returned you may then call decode() on each scanline.
int begin_decoding();
// Returns the next scan line.
// For grayscale images, pScan_line will point to a buffer containing 8-bit pixels (get_bytes_per_pixel() will return 1).
// Otherwise, it will always point to a buffer containing 32-bit RGBA pixels (A will always be 255, and get_bytes_per_pixel() will return 4).
// Returns JPGD_SUCCESS if a scan line has been returned.
// Returns JPGD_DONE if all scan lines have been returned.
// Returns JPGD_FAILED if an error occurred. Call get_error_code() for a more info.
int decode(const void** pScan_line, uint* pScan_line_len);
inline jpgd_status get_error_code() const { return m_error_code; }
inline int get_width() const { return m_image_x_size; }
inline int get_height() const { return m_image_y_size; }
inline int get_num_components() const { return m_comps_in_frame; }
inline int get_bytes_per_pixel() const { return m_dest_bytes_per_pixel; }
inline int get_bytes_per_scan_line() const { return m_image_x_size * get_bytes_per_pixel(); }
// Returns the total number of bytes actually consumed by the decoder (which should equal the actual size of the JPEG file).
inline int get_total_bytes_read() const { return m_total_bytes_read; }
private:
jpeg_decoder(const jpeg_decoder &);
jpeg_decoder &operator =(const jpeg_decoder &);
typedef void (*pDecode_block_func)(jpeg_decoder *, int, int, int);
struct huff_tables
{
bool ac_table;
uint look_up[256];
uint look_up2[256];
uint8 code_size[256];
uint tree[512];
};
struct coeff_buf
{
uint8 *pData;
int block_num_x, block_num_y;
int block_len_x, block_len_y;
int block_size;
};
struct mem_block
{
mem_block *m_pNext;
size_t m_used_count;
size_t m_size;
char m_data[1];
};
jmp_buf m_jmp_state;
mem_block *m_pMem_blocks;
int m_image_x_size;
int m_image_y_size;
jpeg_decoder_stream *m_pStream;
int m_progressive_flag;
uint8 m_huff_ac[JPGD_MAX_HUFF_TABLES];
uint8* m_huff_num[JPGD_MAX_HUFF_TABLES]; // pointer to number of Huffman codes per bit size
uint8* m_huff_val[JPGD_MAX_HUFF_TABLES]; // pointer to Huffman codes per bit size
jpgd_quant_t* m_quant[JPGD_MAX_QUANT_TABLES]; // pointer to quantization tables
int m_scan_type; // Gray, Yh1v1, Yh1v2, Yh2v1, Yh2v2 (CMYK111, CMYK4114 no longer supported)
int m_comps_in_frame; // # of components in frame
int m_comp_h_samp[JPGD_MAX_COMPONENTS]; // component's horizontal sampling factor
int m_comp_v_samp[JPGD_MAX_COMPONENTS]; // component's vertical sampling factor
int m_comp_quant[JPGD_MAX_COMPONENTS]; // component's quantization table selector
int m_comp_ident[JPGD_MAX_COMPONENTS]; // component's ID
int m_comp_h_blocks[JPGD_MAX_COMPONENTS];
int m_comp_v_blocks[JPGD_MAX_COMPONENTS];
int m_comps_in_scan; // # of components in scan
int m_comp_list[JPGD_MAX_COMPS_IN_SCAN]; // components in this scan
int m_comp_dc_tab[JPGD_MAX_COMPONENTS]; // component's DC Huffman coding table selector
int m_comp_ac_tab[JPGD_MAX_COMPONENTS]; // component's AC Huffman coding table selector
int m_spectral_start; // spectral selection start
int m_spectral_end; // spectral selection end
int m_successive_low; // successive approximation low
int m_successive_high; // successive approximation high
int m_max_mcu_x_size; // MCU's max. X size in pixels
int m_max_mcu_y_size; // MCU's max. Y size in pixels
int m_blocks_per_mcu;
int m_max_blocks_per_row;
int m_mcus_per_row, m_mcus_per_col;
int m_mcu_org[JPGD_MAX_BLOCKS_PER_MCU];
int m_total_lines_left; // total # lines left in image
int m_mcu_lines_left; // total # lines left in this MCU
int m_real_dest_bytes_per_scan_line;
int m_dest_bytes_per_scan_line; // rounded up
int m_dest_bytes_per_pixel; // 4 (RGB) or 1 (Y)
huff_tables* m_pHuff_tabs[JPGD_MAX_HUFF_TABLES];
coeff_buf* m_dc_coeffs[JPGD_MAX_COMPONENTS];
coeff_buf* m_ac_coeffs[JPGD_MAX_COMPONENTS];
int m_eob_run;
int m_block_y_mcu[JPGD_MAX_COMPONENTS];
uint8* m_pIn_buf_ofs;
int m_in_buf_left;
int m_tem_flag;
bool m_eof_flag;
uint8 m_in_buf_pad_start[128];
uint8 m_in_buf[JPGD_IN_BUF_SIZE + 128];
uint8 m_in_buf_pad_end[128];
int m_bits_left;
uint m_bit_buf;
int m_restart_interval;
int m_restarts_left;
int m_next_restart_num;
int m_max_mcus_per_row;
int m_max_blocks_per_mcu;
int m_expanded_blocks_per_mcu;
int m_expanded_blocks_per_row;
int m_expanded_blocks_per_component;
bool m_freq_domain_chroma_upsample;
int m_max_mcus_per_col;
uint m_last_dc_val[JPGD_MAX_COMPONENTS];
jpgd_block_t* m_pMCU_coefficients;
int m_mcu_block_max_zag[JPGD_MAX_BLOCKS_PER_MCU];
uint8* m_pSample_buf;
int m_crr[256];
int m_cbb[256];
int m_crg[256];
int m_cbg[256];
uint8* m_pScan_line_0;
uint8* m_pScan_line_1;
jpgd_status m_error_code;
bool m_ready_flag;
int m_total_bytes_read;
void free_all_blocks();
JPGD_NORETURN void stop_decoding(jpgd_status status);
void *alloc(size_t n, bool zero = false);
void word_clear(void *p, uint16 c, uint n);
void prep_in_buffer();
void read_dht_marker();
void read_dqt_marker();
void read_sof_marker();
void skip_variable_marker();
void read_dri_marker();
void read_sos_marker();
int next_marker();
int process_markers();
void locate_soi_marker();
void locate_sof_marker();
int locate_sos_marker();
void init(jpeg_decoder_stream * pStream);
void create_look_ups();
void fix_in_buffer();
void transform_mcu(int mcu_row);
void transform_mcu_expand(int mcu_row);
coeff_buf* coeff_buf_open(int block_num_x, int block_num_y, int block_len_x, int block_len_y);
inline jpgd_block_t *coeff_buf_getp(coeff_buf *cb, int block_x, int block_y);
void load_next_row();
void decode_next_row();
void make_huff_table(int index, huff_tables *pH);
void check_quant_tables();
void check_huff_tables();
void calc_mcu_block_order();
int init_scan();
void init_frame();
void process_restart();
void decode_scan(pDecode_block_func decode_block_func);
void init_progressive();
void init_sequential();
void decode_start();
void decode_init(jpeg_decoder_stream * pStream);
void H2V2Convert();
void H2V1Convert();
void H1V2Convert();
void H1V1Convert();
void gray_convert();
void expanded_convert();
void find_eoi();
inline uint get_char();
inline uint get_char(bool *pPadding_flag);
inline void stuff_char(uint8 q);
inline uint8 get_octet();
inline uint get_bits(int num_bits);
inline uint get_bits_no_markers(int numbits);
inline int huff_decode(huff_tables *pH);
inline int huff_decode(huff_tables *pH, int& extrabits);
static inline uint8 clamp(int i);
static void decode_block_dc_first(jpeg_decoder *pD, int component_id, int block_x, int block_y);
static void decode_block_dc_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y);
static void decode_block_ac_first(jpeg_decoder *pD, int component_id, int block_x, int block_y);
static void decode_block_ac_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y);
};
} // namespace jpgd
#endif // JPEG_DECODER_H
+1044
View File
File diff suppressed because it is too large Load Diff
+169
View File
@@ -0,0 +1,169 @@
// jpge.h - C++ class for JPEG compression.
// Public domain, Rich Geldreich <richgel99@gmail.com>
// Alex Evans: Added RGBA support, linear memory allocator.
#ifndef JPEG_ENCODER_H
#define JPEG_ENCODER_H
namespace jpge
{
typedef unsigned char uint8;
typedef signed short int16;
typedef signed int int32;
typedef unsigned short uint16;
typedef unsigned int uint32;
typedef unsigned int uint;
// JPEG chroma subsampling factors. Y_ONLY (grayscale images) and H2V2 (color images) are the most common.
enum subsampling_t { Y_ONLY = 0, H1V1 = 1, H2V1 = 2, H2V2 = 3 };
// JPEG compression parameters structure.
struct params
{
inline params() : m_quality(85), m_subsampling(H2V2), m_no_chroma_discrim_flag(false), m_two_pass_flag(false) { }
inline bool check() const
{
if ((m_quality < 1) || (m_quality > 100)) return false;
if ((uint)m_subsampling > (uint)H2V2) return false;
return true;
}
// Quality: 1-100, higher is better. Typical values are around 50-95.
int m_quality;
// m_subsampling:
// 0 = Y (grayscale) only
// 1 = YCbCr, no subsampling (H1V1, YCbCr 1x1x1, 3 blocks per MCU)
// 2 = YCbCr, H2V1 subsampling (YCbCr 2x1x1, 4 blocks per MCU)
// 3 = YCbCr, H2V2 subsampling (YCbCr 4x1x1, 6 blocks per MCU-- very common)
subsampling_t m_subsampling;
// Disables CbCr discrimination - only intended for testing.
// If true, the Y quantization table is also used for the CbCr channels.
bool m_no_chroma_discrim_flag;
bool m_two_pass_flag;
};
// Writes JPEG image to a file.
// num_channels must be 1 (Y) or 3 (RGB), image pitch must be width*num_channels.
bool compress_image_to_jpeg_file(const char *pFilename, int width, int height, int num_channels, const uint8 *pImage_data, const params &comp_params = params());
// Writes JPEG image to memory buffer.
// On entry, buf_size is the size of the output buffer pointed at by pBuf, which should be at least ~1024 bytes.
// If return value is true, buf_size will be set to the size of the compressed data.
bool compress_image_to_jpeg_file_in_memory(void *pBuf, int &buf_size, int width, int height, int num_channels, const uint8 *pImage_data, const params &comp_params = params());
// Output stream abstract class - used by the jpeg_encoder class to write to the output stream.
// put_buf() is generally called with len==JPGE_OUT_BUF_SIZE bytes, but for headers it'll be called with smaller amounts.
class output_stream
{
public:
virtual ~output_stream() { };
virtual bool put_buf(const void* Pbuf, int len) = 0;
template<class T> inline bool put_obj(const T& obj) { return put_buf(&obj, sizeof(T)); }
};
// Lower level jpeg_encoder class - useful if more control is needed than the above helper functions.
class jpeg_encoder
{
public:
jpeg_encoder();
~jpeg_encoder();
// Initializes the compressor.
// pStream: The stream object to use for writing compressed data.
// params - Compression parameters structure, defined above.
// width, height - Image dimensions.
// channels - May be 1, or 3. 1 indicates grayscale, 3 indicates RGB source data.
// Returns false on out of memory or if a stream write fails.
bool init(output_stream *pStream, int width, int height, int src_channels, const params &comp_params = params());
const params &get_params() const { return m_params; }
// Deinitializes the compressor, freeing any allocated memory. May be called at any time.
void deinit();
uint get_total_passes() const { return m_params.m_two_pass_flag ? 2 : 1; }
inline uint get_cur_pass() { return m_pass_num; }
// Call this method with each source scanline.
// width * src_channels bytes per scanline is expected (RGB or Y format).
// You must call with NULL after all scanlines are processed to finish compression.
// Returns false on out of memory or if a stream write fails.
bool process_scanline(const void* pScanline);
private:
jpeg_encoder(const jpeg_encoder &);
jpeg_encoder &operator =(const jpeg_encoder &);
typedef int32 sample_array_t;
output_stream *m_pStream;
params m_params;
uint8 m_num_components;
uint8 m_comp_h_samp[3], m_comp_v_samp[3];
int m_image_x, m_image_y, m_image_bpp, m_image_bpl;
int m_image_x_mcu, m_image_y_mcu;
int m_image_bpl_xlt, m_image_bpl_mcu;
int m_mcus_per_row;
int m_mcu_x, m_mcu_y;
uint8 *m_mcu_lines[16];
uint8 m_mcu_y_ofs;
sample_array_t m_sample_array[64];
int16 m_coefficient_array[64];
int32 m_quantization_tables[2][64];
uint m_huff_codes[4][256];
uint8 m_huff_code_sizes[4][256];
uint8 m_huff_bits[4][17];
uint8 m_huff_val[4][256];
uint32 m_huff_count[4][256];
int m_last_dc_val[3];
enum { JPGE_OUT_BUF_SIZE = 2048 };
uint8 m_out_buf[JPGE_OUT_BUF_SIZE];
uint8 *m_pOut_buf;
uint m_out_buf_left;
uint32 m_bit_buffer;
uint m_bits_in;
uint8 m_pass_num;
bool m_all_stream_writes_succeeded;
void optimize_huffman_table(int table_num, int table_len);
void emit_byte(uint8 i);
void emit_word(uint i);
void emit_marker(int marker);
void emit_jfif_app0();
void emit_dqt();
void emit_sof();
void emit_dht(uint8 *bits, uint8 *val, int index, bool ac_flag);
void emit_dhts();
void emit_sos();
void emit_markers();
void compute_huffman_table(uint *codes, uint8 *code_sizes, uint8 *bits, uint8 *val);
void compute_quant_table(int32 *dst, int16 *src);
void adjust_quant_table(int32 *dst, int32 *src);
void first_pass_init();
bool second_pass_init();
bool jpg_open(int p_x_res, int p_y_res, int src_channels);
void load_block_8_8_grey(int x);
void load_block_8_8(int x, int y, int c);
void load_block_16_8(int x, int c);
void load_block_16_8_8(int x, int c);
void load_quantized_coefficients(int component_num);
void flush_output_buffer();
void put_bits(uint bits, uint len);
void code_coefficients_pass_one(int component_num);
void code_coefficients_pass_two(int component_num);
void code_block(int component_num);
void process_mcu_row();
bool terminate_pass_one();
bool terminate_pass_two();
bool process_end_of_image();
void load_mcu(const void* src);
void clear();
void init();
};
} // namespace jpge
#endif // JPEG_ENCODER
+920
View File
@@ -0,0 +1,920 @@
// File: crn_ktx_texture.cpp
#include "crn_core.h"
#include "crn_ktx_texture.h"
#include "crn_console.h"
// Set #if CRNLIB_KTX_PVRTEX_WORKAROUNDS to 1 to enable various workarounds for oddball KTX files written by PVRTexTool.
#define CRNLIB_KTX_PVRTEX_WORKAROUNDS 1
namespace crnlib
{
const uint8 s_ktx_file_id[12] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
bool is_packed_pixel_ogl_type(uint32 ogl_type)
{
switch (ogl_type)
{
case KTX_UNSIGNED_BYTE_3_3_2:
case KTX_UNSIGNED_BYTE_2_3_3_REV:
case KTX_UNSIGNED_SHORT_5_6_5:
case KTX_UNSIGNED_SHORT_5_6_5_REV:
case KTX_UNSIGNED_SHORT_4_4_4_4:
case KTX_UNSIGNED_SHORT_4_4_4_4_REV:
case KTX_UNSIGNED_SHORT_5_5_5_1:
case KTX_UNSIGNED_SHORT_1_5_5_5_REV:
case KTX_UNSIGNED_INT_8_8_8_8:
case KTX_UNSIGNED_INT_8_8_8_8_REV:
case KTX_UNSIGNED_INT_10_10_10_2:
case KTX_UNSIGNED_INT_2_10_10_10_REV:
case KTX_UNSIGNED_INT_24_8:
case KTX_UNSIGNED_INT_10F_11F_11F_REV:
case KTX_UNSIGNED_INT_5_9_9_9_REV:
return true;
}
return false;
}
uint get_ogl_type_size(uint32 ogl_type)
{
switch (ogl_type)
{
case KTX_UNSIGNED_BYTE:
case KTX_BYTE:
return 1;
case KTX_HALF_FLOAT:
case KTX_UNSIGNED_SHORT:
case KTX_SHORT:
return 2;
case KTX_FLOAT:
case KTX_UNSIGNED_INT:
case KTX_INT:
return 4;
case KTX_UNSIGNED_BYTE_3_3_2:
case KTX_UNSIGNED_BYTE_2_3_3_REV:
return 1;
case KTX_UNSIGNED_SHORT_5_6_5:
case KTX_UNSIGNED_SHORT_5_6_5_REV:
case KTX_UNSIGNED_SHORT_4_4_4_4:
case KTX_UNSIGNED_SHORT_4_4_4_4_REV:
case KTX_UNSIGNED_SHORT_5_5_5_1:
case KTX_UNSIGNED_SHORT_1_5_5_5_REV:
return 2;
case KTX_UNSIGNED_INT_8_8_8_8:
case KTX_UNSIGNED_INT_8_8_8_8_REV:
case KTX_UNSIGNED_INT_10_10_10_2:
case KTX_UNSIGNED_INT_2_10_10_10_REV:
case KTX_UNSIGNED_INT_24_8:
case KTX_UNSIGNED_INT_10F_11F_11F_REV:
case KTX_UNSIGNED_INT_5_9_9_9_REV:
return 4;
}
return 0;
}
uint32 get_ogl_base_internal_fmt(uint32 ogl_fmt)
{
switch (ogl_fmt)
{
case KTX_ETC1_RGB8_OES:
case KTX_RGB_S3TC:
case KTX_RGB4_S3TC:
case KTX_COMPRESSED_RGB_S3TC_DXT1_EXT:
case KTX_COMPRESSED_SRGB_S3TC_DXT1_EXT:
return KTX_RGB;
case KTX_COMPRESSED_RGBA_S3TC_DXT1_EXT:
case KTX_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
case KTX_RGBA_S3TC:
case KTX_RGBA4_S3TC:
case KTX_COMPRESSED_RGBA_S3TC_DXT3_EXT:
case KTX_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
case KTX_COMPRESSED_RGBA_S3TC_DXT5_EXT:
case KTX_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
case KTX_RGBA_DXT5_S3TC:
case KTX_RGBA4_DXT5_S3TC:
return KTX_RGBA;
case 1:
case KTX_RED:
case KTX_RED_INTEGER:
case KTX_GREEN:
case KTX_GREEN_INTEGER:
case KTX_BLUE:
case KTX_BLUE_INTEGER:
case KTX_R8:
case KTX_R8UI:
case KTX_LUMINANCE8:
case KTX_ALPHA:
case KTX_LUMINANCE:
case KTX_COMPRESSED_RED_RGTC1_EXT:
case KTX_COMPRESSED_SIGNED_RED_RGTC1_EXT:
case KTX_COMPRESSED_LUMINANCE_LATC1_EXT:
case KTX_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT:
return KTX_RED;
case 2:
case KTX_RG:
case KTX_RG8:
case KTX_RG_INTEGER:
case KTX_LUMINANCE_ALPHA:
case KTX_COMPRESSED_RED_GREEN_RGTC2_EXT:
case KTX_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT:
case KTX_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT:
case KTX_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT:
return KTX_RG;
case 3:
case KTX_SRGB:
case KTX_RGB:
case KTX_RGB_INTEGER:
case KTX_BGR:
case KTX_BGR_INTEGER:
case KTX_RGB8:
case KTX_SRGB8:
return KTX_RGB;
case 4:
case KTX_RGBA:
case KTX_BGRA:
case KTX_RGBA_INTEGER:
case KTX_BGRA_INTEGER:
case KTX_SRGB_ALPHA:
case KTX_SRGB8_ALPHA8:
case KTX_RGBA8:
return KTX_RGBA;
}
return 0;
}
bool get_ogl_fmt_desc(uint32 ogl_fmt, uint32 ogl_type, uint& block_dim, uint& bytes_per_block)
{
uint ogl_type_size = get_ogl_type_size(ogl_type);
block_dim = 1;
bytes_per_block = 0;
switch (ogl_fmt)
{
case KTX_COMPRESSED_RED_RGTC1_EXT:
case KTX_COMPRESSED_SIGNED_RED_RGTC1_EXT:
case KTX_COMPRESSED_LUMINANCE_LATC1_EXT:
case KTX_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT:
case KTX_ETC1_RGB8_OES:
case KTX_RGB_S3TC:
case KTX_RGB4_S3TC:
case KTX_COMPRESSED_RGB_S3TC_DXT1_EXT:
case KTX_COMPRESSED_RGBA_S3TC_DXT1_EXT:
case KTX_COMPRESSED_SRGB_S3TC_DXT1_EXT:
case KTX_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
{
block_dim = 4;
bytes_per_block = 8;
break;
}
case KTX_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT:
case KTX_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT:
case KTX_COMPRESSED_RED_GREEN_RGTC2_EXT:
case KTX_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT:
case KTX_RGBA_S3TC:
case KTX_RGBA4_S3TC:
case KTX_COMPRESSED_RGBA_S3TC_DXT3_EXT:
case KTX_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
case KTX_COMPRESSED_RGBA_S3TC_DXT5_EXT:
case KTX_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
case KTX_RGBA_DXT5_S3TC:
case KTX_RGBA4_DXT5_S3TC:
{
block_dim = 4;
bytes_per_block = 16;
break;
}
case 1:
case KTX_ALPHA:
case KTX_RED:
case KTX_GREEN:
case KTX_BLUE:
case KTX_RED_INTEGER:
case KTX_GREEN_INTEGER:
case KTX_BLUE_INTEGER:
case KTX_LUMINANCE:
{
bytes_per_block = ogl_type_size;
break;
}
case KTX_R8:
case KTX_R8UI:
case KTX_ALPHA8:
case KTX_LUMINANCE8:
{
bytes_per_block = 1;
break;
}
case 2:
case KTX_RG:
case KTX_RG_INTEGER:
case KTX_LUMINANCE_ALPHA:
{
bytes_per_block = 2 * ogl_type_size;
break;
}
case KTX_RG8:
case KTX_LUMINANCE8_ALPHA8:
{
bytes_per_block = 2;
break;
}
case 3:
case KTX_SRGB:
case KTX_RGB:
case KTX_BGR:
case KTX_RGB_INTEGER:
case KTX_BGR_INTEGER:
{
bytes_per_block = is_packed_pixel_ogl_type(ogl_type) ? ogl_type_size : (3 * ogl_type_size);
break;
}
case KTX_RGB8:
case KTX_SRGB8:
{
bytes_per_block = 3;
break;
}
case 4:
case KTX_RGBA:
case KTX_BGRA:
case KTX_RGBA_INTEGER:
case KTX_BGRA_INTEGER:
case KTX_SRGB_ALPHA:
{
bytes_per_block = is_packed_pixel_ogl_type(ogl_type) ? ogl_type_size : (4 * ogl_type_size);
break;
}
case KTX_SRGB8_ALPHA8:
case KTX_RGBA8:
{
bytes_per_block = 4;
break;
}
default:
return false;
}
return true;
}
bool ktx_texture::compute_pixel_info()
{
if ((!m_header.m_glType) || (!m_header.m_glFormat))
{
if ((m_header.m_glType) || (m_header.m_glFormat))
return false;
// Must be a compressed format.
if (!get_ogl_fmt_desc(m_header.m_glInternalFormat, m_header.m_glType, m_block_dim, m_bytes_per_block))
{
#if CRNLIB_KTX_PVRTEX_WORKAROUNDS
if ((!m_header.m_glInternalFormat) && (!m_header.m_glType) && (!m_header.m_glTypeSize) && (!m_header.m_glBaseInternalFormat))
{
// PVRTexTool writes bogus headers when outputting ETC1.
console::warning("ktx_texture::compute_pixel_info: Header doesn't specify any format, assuming ETC1 and hoping for the best");
m_header.m_glBaseInternalFormat = KTX_RGB;
m_header.m_glInternalFormat = KTX_ETC1_RGB8_OES;
m_header.m_glTypeSize = 1;
m_block_dim = 4;
m_bytes_per_block = 8;
return true;
}
#endif
return false;
}
if (m_block_dim == 1)
return false;
}
else
{
// Must be an uncompressed format.
if (!get_ogl_fmt_desc(m_header.m_glFormat, m_header.m_glType, m_block_dim, m_bytes_per_block))
return false;
if (m_block_dim > 1)
return false;
}
return true;
}
bool ktx_texture::read_from_stream(data_stream_serializer& serializer)
{
clear();
// Read header
if (serializer.read(&m_header, 1, sizeof(m_header)) != sizeof(ktx_header))
return false;
// Check header
if (memcmp(s_ktx_file_id, m_header.m_identifier, sizeof(m_header.m_identifier)))
return false;
if ((m_header.m_endianness != KTX_OPPOSITE_ENDIAN) && (m_header.m_endianness != KTX_ENDIAN))
return false;
m_opposite_endianness = (m_header.m_endianness == KTX_OPPOSITE_ENDIAN);
if (m_opposite_endianness)
{
m_header.endian_swap();
if ((m_header.m_glTypeSize != sizeof(uint8)) && (m_header.m_glTypeSize != sizeof(uint16)) && (m_header.m_glTypeSize != sizeof(uint32)))
return false;
}
if (!check_header())
return false;
if (!compute_pixel_info())
return false;
uint8 pad_bytes[3];
// Read the key value entries
uint num_key_value_bytes_remaining = m_header.m_bytesOfKeyValueData;
while (num_key_value_bytes_remaining)
{
if (num_key_value_bytes_remaining < sizeof(uint32))
return false;
uint32 key_value_byte_size;
if (serializer.read(&key_value_byte_size, 1, sizeof(uint32)) != sizeof(uint32))
return false;
num_key_value_bytes_remaining -= sizeof(uint32);
if (m_opposite_endianness)
key_value_byte_size = utils::swap32(key_value_byte_size);
if (key_value_byte_size > num_key_value_bytes_remaining)
return false;
uint8_vec key_value_data;
if (key_value_byte_size)
{
key_value_data.resize(key_value_byte_size);
if (serializer.read(&key_value_data[0], 1, key_value_byte_size) != key_value_byte_size)
return false;
}
m_key_values.push_back(key_value_data);
uint padding = 3 - ((key_value_byte_size + 3) % 4);
if (padding)
{
if (serializer.read(pad_bytes, 1, padding) != padding)
return false;
}
num_key_value_bytes_remaining -= key_value_byte_size;
if (num_key_value_bytes_remaining < padding)
return false;
num_key_value_bytes_remaining -= padding;
}
// Now read the mip levels
uint total_faces = get_num_mips() * get_array_size() * get_num_faces() * get_depth();
if ((!total_faces) || (total_faces > 65535))
return false;
// See Section 2.8 of KTX file format: No rounding to block sizes should be applied for block compressed textures.
// OK, I'm going to break that rule otherwise KTX can only store a subset of textures that DDS can handle for no good reason.
#if 0
const uint mip0_row_blocks = m_header.m_pixelWidth / m_block_dim;
const uint mip0_col_blocks = CRNLIB_MAX(1, m_header.m_pixelHeight) / m_block_dim;
#else
const uint mip0_row_blocks = (m_header.m_pixelWidth + m_block_dim - 1) / m_block_dim;
const uint mip0_col_blocks = (CRNLIB_MAX(1, m_header.m_pixelHeight) + m_block_dim - 1) / m_block_dim;
#endif
if ((!mip0_row_blocks) || (!mip0_col_blocks))
return false;
const uint mip0_depth = CRNLIB_MAX(1, m_header.m_pixelDepth); mip0_depth;
bool has_valid_image_size_fields = true;
bool disable_mip_and_cubemap_padding = false;
#if CRNLIB_KTX_PVRTEX_WORKAROUNDS
{
// PVRTexTool has a bogus KTX writer that doesn't write any imageSize fields. Nice.
size_t expected_bytes_remaining = 0;
for (uint mip_level = 0; mip_level < get_num_mips(); mip_level++)
{
uint mip_width, mip_height, mip_depth;
get_mip_dim(mip_level, mip_width, mip_height, mip_depth);
const uint mip_row_blocks = (mip_width + m_block_dim - 1) / m_block_dim;
const uint mip_col_blocks = (mip_height + m_block_dim - 1) / m_block_dim;
if ((!mip_row_blocks) || (!mip_col_blocks))
return false;
expected_bytes_remaining += sizeof(uint32);
if ((!m_header.m_numberOfArrayElements) && (get_num_faces() == 6))
{
for (uint face = 0; face < get_num_faces(); face++)
{
uint slice_size = mip_row_blocks * mip_col_blocks * m_bytes_per_block;
expected_bytes_remaining += slice_size;
uint num_cube_pad_bytes = 3 - ((slice_size + 3) % 4);
expected_bytes_remaining += num_cube_pad_bytes;
}
}
else
{
uint total_mip_size = 0;
for (uint array_element = 0; array_element < get_array_size(); array_element++)
{
for (uint face = 0; face < get_num_faces(); face++)
{
for (uint zslice = 0; zslice < mip_depth; zslice++)
{
uint slice_size = mip_row_blocks * mip_col_blocks * m_bytes_per_block;
total_mip_size += slice_size;
}
}
}
expected_bytes_remaining += total_mip_size;
uint num_mip_pad_bytes = 3 - ((total_mip_size + 3) % 4);
expected_bytes_remaining += num_mip_pad_bytes;
}
}
if (serializer.get_stream()->get_remaining() < expected_bytes_remaining)
{
has_valid_image_size_fields = false;
disable_mip_and_cubemap_padding = true;
console::warning("ktx_texture::read_from_stream: KTX file size is smaller than expected - trying to read anyway without imageSize fields");
}
}
#endif
for (uint mip_level = 0; mip_level < get_num_mips(); mip_level++)
{
uint mip_width, mip_height, mip_depth;
get_mip_dim(mip_level, mip_width, mip_height, mip_depth);
const uint mip_row_blocks = (mip_width + m_block_dim - 1) / m_block_dim;
const uint mip_col_blocks = (mip_height + m_block_dim - 1) / m_block_dim;
if ((!mip_row_blocks) || (!mip_col_blocks))
return false;
uint32 image_size = 0;
if (!has_valid_image_size_fields)
image_size = mip_depth * mip_row_blocks * mip_col_blocks * m_bytes_per_block * get_array_size() * get_num_faces();
else
{
if (serializer.read(&image_size, 1, sizeof(image_size)) != sizeof(image_size))
return false;
if (m_opposite_endianness)
image_size = utils::swap32(image_size);
}
if (!image_size)
return false;
uint total_mip_size = 0;
if ((!m_header.m_numberOfArrayElements) && (get_num_faces() == 6))
{
// plain non-array cubemap
for (uint face = 0; face < get_num_faces(); face++)
{
CRNLIB_ASSERT(m_image_data.size() == get_image_index(mip_level, 0, face, 0));
m_image_data.push_back(uint8_vec());
uint8_vec& image_data = m_image_data.back();
image_data.resize(image_size);
if (serializer.read(&image_data[0], 1, image_size) != image_size)
return false;
if (m_opposite_endianness)
utils::endian_swap_mem(&image_data[0], image_size, m_header.m_glTypeSize);
uint num_cube_pad_bytes = disable_mip_and_cubemap_padding ? 0 : (3 - ((image_size + 3) % 4));
if (serializer.read(pad_bytes, 1, num_cube_pad_bytes) != num_cube_pad_bytes)
return false;
total_mip_size += image_size + num_cube_pad_bytes;
}
}
else
{
// 1D, 2D, 3D (normal or array texture), or array cubemap
uint num_image_bytes_remaining = image_size;
for (uint array_element = 0; array_element < get_array_size(); array_element++)
{
for (uint face = 0; face < get_num_faces(); face++)
{
for (uint zslice = 0; zslice < mip_depth; zslice++)
{
CRNLIB_ASSERT(m_image_data.size() == get_image_index(mip_level, array_element, face, zslice));
uint slice_size = mip_row_blocks * mip_col_blocks * m_bytes_per_block;
if ((!slice_size) || (slice_size > num_image_bytes_remaining))
return false;
m_image_data.push_back(uint8_vec());
uint8_vec& image_data = m_image_data.back();
image_data.resize(slice_size);
if (serializer.read(&image_data[0], 1, slice_size) != slice_size)
return false;
if (m_opposite_endianness)
utils::endian_swap_mem(&image_data[0], slice_size, m_header.m_glTypeSize);
num_image_bytes_remaining -= slice_size;
total_mip_size += slice_size;
}
}
}
if (num_image_bytes_remaining)
return false;
}
uint num_mip_pad_bytes = disable_mip_and_cubemap_padding ? 0 : (3 - ((total_mip_size + 3) % 4));
if (serializer.read(pad_bytes, 1, num_mip_pad_bytes) != num_mip_pad_bytes)
return false;
}
return true;
}
bool ktx_texture::write_to_stream(data_stream_serializer& serializer, bool no_keyvalue_data)
{
if (!consistency_check())
{
CRNLIB_ASSERT(0);
return false;
}
memcpy(m_header.m_identifier, s_ktx_file_id, sizeof(m_header.m_identifier));
m_header.m_endianness = m_opposite_endianness ? KTX_OPPOSITE_ENDIAN : KTX_ENDIAN;
if (m_block_dim == 1)
{
m_header.m_glTypeSize = get_ogl_type_size(m_header.m_glType);
m_header.m_glBaseInternalFormat = m_header.m_glFormat;
}
else
{
m_header.m_glBaseInternalFormat = get_ogl_base_internal_fmt(m_header.m_glInternalFormat);
}
m_header.m_bytesOfKeyValueData = 0;
if (!no_keyvalue_data)
{
for (uint i = 0; i < m_key_values.size(); i++)
m_header.m_bytesOfKeyValueData += sizeof(uint32) + ((m_key_values[i].size() + 3) & ~3);
}
if (m_opposite_endianness)
m_header.endian_swap();
bool success = (serializer.write(&m_header, sizeof(m_header), 1) == 1);
if (m_opposite_endianness)
m_header.endian_swap();
if (!success)
return success;
uint total_key_value_bytes = 0;
const uint8 padding[3] = { 0, 0, 0 };
if (!no_keyvalue_data)
{
for (uint i = 0; i < m_key_values.size(); i++)
{
uint32 key_value_size = m_key_values[i].size();
if (m_opposite_endianness)
key_value_size = utils::swap32(key_value_size);
success = (serializer.write(&key_value_size, sizeof(key_value_size), 1) == 1);
total_key_value_bytes += sizeof(key_value_size);
if (m_opposite_endianness)
key_value_size = utils::swap32(key_value_size);
if (!success)
return false;
if (key_value_size)
{
if (serializer.write(&m_key_values[i][0], key_value_size, 1) != 1)
return false;
total_key_value_bytes += key_value_size;
uint num_padding = 3 - ((key_value_size + 3) % 4);
if ((num_padding) && (serializer.write(padding, num_padding, 1) != 1))
return false;
total_key_value_bytes += num_padding;
}
}
(void)total_key_value_bytes;
}
CRNLIB_ASSERT(total_key_value_bytes == m_header.m_bytesOfKeyValueData);
for (uint mip_level = 0; mip_level < get_num_mips(); mip_level++)
{
uint mip_width, mip_height, mip_depth;
get_mip_dim(mip_level, mip_width, mip_height, mip_depth);
const uint mip_row_blocks = (mip_width + m_block_dim - 1) / m_block_dim;
const uint mip_col_blocks = (mip_height + m_block_dim - 1) / m_block_dim;
if ((!mip_row_blocks) || (!mip_col_blocks))
return false;
uint32 image_size = mip_row_blocks * mip_col_blocks * m_bytes_per_block;
if ((m_header.m_numberOfArrayElements) || (get_num_faces() == 1))
image_size *= (get_array_size() * get_num_faces() * get_depth());
if (!image_size)
return false;
if (m_opposite_endianness)
image_size = utils::swap32(image_size);
success = (serializer.write(&image_size, sizeof(image_size), 1) == 1);
if (m_opposite_endianness)
image_size = utils::swap32(image_size);
if (!success)
return false;
uint total_mip_size = 0;
if ((!m_header.m_numberOfArrayElements) && (get_num_faces() == 6))
{
// plain non-array cubemap
for (uint face = 0; face < get_num_faces(); face++)
{
const uint8_vec& image_data = get_image_data(get_image_index(mip_level, 0, face, 0));
if ((!image_data.size()) || (image_data.size() != image_size))
return false;
if (m_opposite_endianness)
{
uint8_vec tmp_image_data(image_data);
utils::endian_swap_mem(&tmp_image_data[0], tmp_image_data.size(), m_header.m_glTypeSize);
if (serializer.write(&tmp_image_data[0], tmp_image_data.size(), 1) != 1)
return false;
}
else if (serializer.write(&image_data[0], image_data.size(), 1) != 1)
return false;
uint num_cube_pad_bytes = 3 - ((image_data.size() + 3) % 4);
if ((num_cube_pad_bytes) && (serializer.write(padding, num_cube_pad_bytes, 1) != 1))
return false;
total_mip_size += image_size + num_cube_pad_bytes;
}
}
else
{
// 1D, 2D, 3D (normal or array texture), or array cubemap
for (uint array_element = 0; array_element < get_array_size(); array_element++)
{
for (uint face = 0; face < get_num_faces(); face++)
{
for (uint zslice = 0; zslice < mip_depth; zslice++)
{
const uint8_vec& image_data = get_image_data(get_image_index(mip_level, array_element, face, zslice));
if (!image_data.size())
return false;
if (m_opposite_endianness)
{
uint8_vec tmp_image_data(image_data);
utils::endian_swap_mem(&tmp_image_data[0], tmp_image_data.size(), m_header.m_glTypeSize);
if (serializer.write(&tmp_image_data[0], tmp_image_data.size(), 1) != 1)
return false;
}
else if (serializer.write(&image_data[0], image_data.size(), 1) != 1)
return false;
total_mip_size += image_data.size();
}
}
}
uint num_mip_pad_bytes = 3 - ((total_mip_size + 3) % 4);
if ((num_mip_pad_bytes) && (serializer.write(padding, num_mip_pad_bytes, 1) != 1))
return false;
total_mip_size += num_mip_pad_bytes;
}
CRNLIB_ASSERT((total_mip_size & 3) == 0);
}
return true;
}
bool ktx_texture::init_2D(uint width, uint height, uint num_mips, uint32 ogl_internal_fmt, uint32 ogl_fmt, uint32 ogl_type)
{
clear();
m_header.m_pixelWidth = width;
m_header.m_pixelHeight = height;
m_header.m_numberOfMipmapLevels = num_mips;
m_header.m_glInternalFormat = ogl_internal_fmt;
m_header.m_glFormat = ogl_fmt;
m_header.m_glType = ogl_type;
m_header.m_numberOfFaces = 1;
if (!compute_pixel_info())
return false;
return true;
}
bool ktx_texture::init_2D_array(uint width, uint height, uint num_mips, uint array_size, uint32 ogl_internal_fmt, uint32 ogl_fmt, uint32 ogl_type)
{
clear();
m_header.m_pixelWidth = width;
m_header.m_pixelHeight = height;
m_header.m_numberOfMipmapLevels = num_mips;
m_header.m_numberOfArrayElements = array_size;
m_header.m_glInternalFormat = ogl_internal_fmt;
m_header.m_glFormat = ogl_fmt;
m_header.m_glType = ogl_type;
m_header.m_numberOfFaces = 1;
if (!compute_pixel_info())
return false;
return true;
}
bool ktx_texture::init_3D(uint width, uint height, uint depth, uint num_mips, uint32 ogl_internal_fmt, uint32 ogl_fmt, uint32 ogl_type)
{
clear();
m_header.m_pixelWidth = width;
m_header.m_pixelHeight = height;
m_header.m_pixelDepth = depth;
m_header.m_numberOfMipmapLevels = num_mips;
m_header.m_glInternalFormat = ogl_internal_fmt;
m_header.m_glFormat = ogl_fmt;
m_header.m_glType = ogl_type;
m_header.m_numberOfFaces = 1;
if (!compute_pixel_info())
return false;
return true;
}
bool ktx_texture::init_cubemap(uint dim, uint num_mips, uint32 ogl_internal_fmt, uint32 ogl_fmt, uint32 ogl_type)
{
clear();
m_header.m_pixelWidth = dim;
m_header.m_pixelHeight = dim;
m_header.m_numberOfMipmapLevels = num_mips;
m_header.m_glInternalFormat = ogl_internal_fmt;
m_header.m_glFormat = ogl_fmt;
m_header.m_glType = ogl_type;
m_header.m_numberOfFaces = 6;
if (!compute_pixel_info())
return false;
return true;
}
bool ktx_texture::check_header() const
{
if (((get_num_faces() != 1) && (get_num_faces() != 6)) || (!m_header.m_pixelWidth))
return false;
if ((!m_header.m_pixelHeight) && (m_header.m_pixelDepth))
return false;
if ((get_num_faces() == 6) && ((m_header.m_pixelDepth) || (!m_header.m_pixelHeight)))
return false;
if (m_header.m_numberOfMipmapLevels)
{
const uint max_mipmap_dimension = 1U << (m_header.m_numberOfMipmapLevels - 1U);
if (max_mipmap_dimension > (CRNLIB_MAX(CRNLIB_MAX(m_header.m_pixelWidth, m_header.m_pixelHeight), m_header.m_pixelDepth)))
return false;
}
return true;
}
bool ktx_texture::consistency_check() const
{
if (!check_header())
return false;
uint block_dim = 0, bytes_per_block = 0;
if ((!m_header.m_glType) || (!m_header.m_glFormat))
{
if ((m_header.m_glType) || (m_header.m_glFormat))
return false;
if (!get_ogl_fmt_desc(m_header.m_glInternalFormat, m_header.m_glType, block_dim, bytes_per_block))
return false;
if (block_dim == 1)
return false;
//if ((get_width() % block_dim) || (get_height() % block_dim))
// return false;
}
else
{
if (!get_ogl_fmt_desc(m_header.m_glFormat, m_header.m_glType, block_dim, bytes_per_block))
return false;
if (block_dim > 1)
return false;
}
if ((m_block_dim != block_dim) || (m_bytes_per_block != bytes_per_block))
return false;
if (m_image_data.size() != get_total_images())
return false;
for (uint mip_level = 0; mip_level < get_num_mips(); mip_level++)
{
uint mip_width, mip_height, mip_depth;
get_mip_dim(mip_level, mip_width, mip_height, mip_depth);
const uint mip_row_blocks = (mip_width + m_block_dim - 1) / m_block_dim;
const uint mip_col_blocks = (mip_height + m_block_dim - 1) / m_block_dim;
if ((!mip_row_blocks) || (!mip_col_blocks))
return false;
for (uint array_element = 0; array_element < get_array_size(); array_element++)
{
for (uint face = 0; face < get_num_faces(); face++)
{
for (uint zslice = 0; zslice < mip_depth; zslice++)
{
const uint8_vec& image_data = get_image_data(get_image_index(mip_level, array_element, face, zslice));
uint expected_image_size = mip_row_blocks * mip_col_blocks * m_bytes_per_block;
if (image_data.size() != expected_image_size)
return false;
}
}
}
}
return true;
}
const uint8_vec* ktx_texture::find_key(const char* pKey) const
{
const size_t n = strlen(pKey) + 1;
for (uint i = 0; i < m_key_values.size(); i++)
{
const uint8_vec& v = m_key_values[i];
if ((v.size() >= n) && (!memcmp(&v[0], pKey, n)))
return &v;
}
return NULL;
}
bool ktx_texture::get_key_value_as_string(const char* pKey, dynamic_string& str) const
{
const uint8_vec* p = find_key(pKey);
if (!p)
{
str.clear();
return false;
}
const uint ofs = (static_cast<uint>(strlen(pKey)) + 1);
const uint8* pValue = p->get_ptr() + ofs;
const uint n = p->size() - ofs;
uint i;
for (i = 0; i < n; i++)
if (!pValue[i])
break;
str.set_from_buf(pValue, i);
return true;
}
uint ktx_texture::add_key_value(const char* pKey, const void* pVal, uint val_size)
{
const uint idx = m_key_values.size();
m_key_values.resize(idx + 1);
uint8_vec& v = m_key_values.back();
v.append(reinterpret_cast<const uint8*>(pKey), static_cast<uint>(strlen(pKey)) + 1);
v.append(static_cast<const uint8*>(pVal), val_size);
return idx;
}
} // namespace crnlib
+244
View File
@@ -0,0 +1,244 @@
// File: crn_ktx_texture.h
#ifndef _KTX_TEXTURE_H_
#define _KTX_TEXTURE_H_
#ifdef _MSC_VER
#pragma once
#endif
#include "crn_data_stream_serializer.h"
#define KTX_ENDIAN 0x04030201
#define KTX_OPPOSITE_ENDIAN 0x01020304
namespace crnlib
{
extern const uint8 s_ktx_file_id[12];
struct ktx_header
{
uint8 m_identifier[12];
uint32 m_endianness;
uint32 m_glType;
uint32 m_glTypeSize;
uint32 m_glFormat;
uint32 m_glInternalFormat;
uint32 m_glBaseInternalFormat;
uint32 m_pixelWidth;
uint32 m_pixelHeight;
uint32 m_pixelDepth;
uint32 m_numberOfArrayElements;
uint32 m_numberOfFaces;
uint32 m_numberOfMipmapLevels;
uint32 m_bytesOfKeyValueData;
void clear()
{
memset(this, 0, sizeof(*this));
}
void endian_swap()
{
utils::endian_swap_mem32(&m_endianness, (sizeof(*this) - sizeof(m_identifier)) / sizeof(uint32));
}
};
typedef crnlib::vector<uint8_vec> ktx_key_value_vec;
typedef crnlib::vector<uint8_vec> ktx_image_data_vec;
// Compressed pixel data formats: ETC1, DXT1, DXT3, DXT5
enum
{
KTX_ETC1_RGB8_OES = 0x8D64, KTX_RGB_S3TC = 0x83A0, KTX_RGB4_S3TC = 0x83A1, KTX_COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F0,
KTX_COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1, KTX_COMPRESSED_SRGB_S3TC_DXT1_EXT = 0x8C4C, KTX_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT = 0x8C4D,
KTX_RGBA_S3TC = 0x83A2, KTX_RGBA4_S3TC = 0x83A3, KTX_COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2, KTX_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT = 0x8C4E,
KTX_COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3, KTX_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT = 0x8C4F, KTX_RGBA_DXT5_S3TC = 0x83A4, KTX_RGBA4_DXT5_S3TC = 0x83A5,
KTX_COMPRESSED_RED_RGTC1_EXT = 0x8DBB, KTX_COMPRESSED_SIGNED_RED_RGTC1_EXT = 0x8DBC, KTX_COMPRESSED_RED_GREEN_RGTC2_EXT = 0x8DBD, KTX_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT = 0x8DBE,
KTX_COMPRESSED_LUMINANCE_LATC1_EXT = 0x8C70, KTX_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT = 0x8C71, KTX_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT = 0x8C72, KTX_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT = 0x8C73
};
// Pixel formats (various internal, base, and base internal formats)
enum
{
KTX_R8 = 0x8229, KTX_R8UI = 0x8232, KTX_RGB8 = 0x8051, KTX_SRGB8 = 0x8C41, KTX_SRGB = 0x8C40, KTX_SRGB_ALPHA = 0x8C42,
KTX_SRGB8_ALPHA8 = 0x8C43, KTX_RGBA8 = 0x8058, KTX_STENCIL_INDEX = 0x1901, KTX_DEPTH_COMPONENT = 0x1902, KTX_DEPTH_STENCIL = 0x84F9, KTX_RED = 0x1903,
KTX_GREEN = 0x1904, KTX_BLUE = 0x1905, KTX_ALPHA = 0x1906, KTX_RG = 0x8227, KTX_RGB = 0x1907, KTX_RGBA = 0x1908, KTX_BGR = 0x80E0, KTX_BGRA = 0x80E1,
KTX_RED_INTEGER = 0x8D94, KTX_GREEN_INTEGER = 0x8D95, KTX_BLUE_INTEGER = 0x8D96, KTX_ALPHA_INTEGER = 0x8D97, KTX_RGB_INTEGER = 0x8D98, KTX_RGBA_INTEGER = 0x8D99,
KTX_BGR_INTEGER = 0x8D9A, KTX_BGRA_INTEGER = 0x8D9B, KTX_LUMINANCE = 0x1909, KTX_LUMINANCE_ALPHA = 0x190A, KTX_RG_INTEGER = 0x8228, KTX_RG8 = 0x822B,
KTX_ALPHA8 = 0x803C, KTX_LUMINANCE8 = 0x8040, KTX_LUMINANCE8_ALPHA8 = 0x8045
};
// Pixel data types
enum
{
KTX_UNSIGNED_BYTE = 0x1401, KTX_BYTE = 0x1400, KTX_UNSIGNED_SHORT = 0x1403, KTX_SHORT = 0x1402,
KTX_UNSIGNED_INT = 0x1405, KTX_INT = 0x1404, KTX_HALF_FLOAT = 0x140B, KTX_FLOAT = 0x1406,
KTX_UNSIGNED_BYTE_3_3_2 = 0x8032, KTX_UNSIGNED_BYTE_2_3_3_REV = 0x8362, KTX_UNSIGNED_SHORT_5_6_5 = 0x8363,
KTX_UNSIGNED_SHORT_5_6_5_REV = 0x8364, KTX_UNSIGNED_SHORT_4_4_4_4 = 0x8033, KTX_UNSIGNED_SHORT_4_4_4_4_REV = 0x8365,
KTX_UNSIGNED_SHORT_5_5_5_1 = 0x8034, KTX_UNSIGNED_SHORT_1_5_5_5_REV = 0x8366, KTX_UNSIGNED_INT_8_8_8_8 = 0x8035,
KTX_UNSIGNED_INT_8_8_8_8_REV = 0x8367, KTX_UNSIGNED_INT_10_10_10_2 = 0x8036, KTX_UNSIGNED_INT_2_10_10_10_REV = 0x8368,
KTX_UNSIGNED_INT_24_8 = 0x84FA, KTX_UNSIGNED_INT_10F_11F_11F_REV = 0x8C3B, KTX_UNSIGNED_INT_5_9_9_9_REV = 0x8C3E,
KTX_FLOAT_32_UNSIGNED_INT_24_8_REV = 0x8DAD
};
bool is_packed_pixel_ogl_type(uint32 ogl_type);
uint get_ogl_type_size(uint32 ogl_type);
bool get_ogl_fmt_desc(uint32 ogl_fmt, uint32 ogl_type, uint& block_dim, uint& bytes_per_block);
uint get_ogl_type_size(uint32 ogl_type);
uint32 get_ogl_base_internal_fmt(uint32 ogl_fmt);
class ktx_texture
{
public:
ktx_texture()
{
clear();
}
ktx_texture(const ktx_texture& other)
{
*this = other;
}
ktx_texture& operator= (const ktx_texture& rhs)
{
if (this == &rhs)
return *this;
clear();
m_header = rhs.m_header;
m_key_values = rhs.m_key_values;
m_image_data = rhs.m_image_data;
m_block_dim = rhs.m_block_dim;
m_bytes_per_block = rhs.m_bytes_per_block;
m_opposite_endianness = rhs.m_opposite_endianness;
return *this;
}
void clear()
{
m_header.clear();
m_key_values.clear();
m_image_data.clear();
m_block_dim = 0;
m_bytes_per_block = 0;
m_opposite_endianness = false;
}
// High level methods
bool read_from_stream(data_stream_serializer& serializer);
bool write_to_stream(data_stream_serializer& serializer, bool no_keyvalue_data = false);
bool init_2D(uint width, uint height, uint num_mips, uint32 ogl_internal_fmt, uint32 ogl_fmt, uint32 ogl_type);
bool init_2D_array(uint width, uint height, uint num_mips, uint array_size, uint32 ogl_internal_fmt, uint32 ogl_fmt, uint32 ogl_type);
bool init_3D(uint width, uint height, uint depth, uint num_mips, uint32 ogl_internal_fmt, uint32 ogl_fmt, uint32 ogl_type);
bool init_cubemap(uint dim, uint num_mips, uint32 ogl_internal_fmt, uint32 ogl_fmt, uint32 ogl_type);
bool check_header() const;
bool consistency_check() const;
// General info
bool is_valid() const { return (m_header.m_pixelWidth > 0) && (m_image_data.size() > 0); }
uint get_width() const { return m_header.m_pixelWidth; }
uint get_height() const { return CRNLIB_MAX(m_header.m_pixelHeight, 1); }
uint get_depth() const { return CRNLIB_MAX(m_header.m_pixelDepth, 1); }
uint get_num_mips() const { return CRNLIB_MAX(m_header.m_numberOfMipmapLevels, 1); }
uint get_array_size() const { return CRNLIB_MAX(m_header.m_numberOfArrayElements, 1); }
uint get_num_faces() const { return m_header.m_numberOfFaces; }
uint32 get_ogl_type() const { return m_header.m_glType; }
uint32 get_ogl_fmt() const { return m_header.m_glFormat; }
uint32 get_ogl_base_fmt() const { return m_header.m_glBaseInternalFormat; }
uint32 get_ogl_internal_fmt() const { return m_header.m_glInternalFormat; }
uint get_total_images() const { return get_num_mips() * (get_depth() * get_num_faces() * get_array_size()); }
bool is_compressed() const { return m_block_dim > 1; }
bool is_uncompressed() const { return !is_compressed(); }
bool get_opposite_endianness() const { return m_opposite_endianness; }
void set_opposite_endianness(bool flag) { m_opposite_endianness = flag; }
uint32 get_block_dim() const { return m_block_dim; }
uint32 get_bytes_per_block() const { return m_bytes_per_block; }
const ktx_header& get_header() const { return m_header; }
// Key values
const ktx_key_value_vec& get_key_value_vec() const { return m_key_values; }
ktx_key_value_vec& get_key_value_vec() { return m_key_values; }
const uint8_vec* find_key(const char* pKey) const;
bool get_key_value_as_string(const char* pKey, dynamic_string& str) const;
uint add_key_value(const char* pKey, const void* pVal, uint val_size);
uint add_key_value(const char* pKey, const char* pVal) { return add_key_value(pKey, pVal, static_cast<uint>(strlen(pVal)) + 1); }
// Image data
uint get_num_images() const { return m_image_data.size(); }
const uint8_vec& get_image_data(uint image_index) const { return m_image_data[image_index]; }
uint8_vec& get_image_data(uint image_index) { return m_image_data[image_index]; }
const uint8_vec& get_image_data(uint mip_index, uint array_index, uint face_index, uint zslice_index) const { return get_image_data(get_image_index(mip_index, array_index, face_index, zslice_index)); }
uint8_vec& get_image_data(uint mip_index, uint array_index, uint face_index, uint zslice_index) { return get_image_data(get_image_index(mip_index, array_index, face_index, zslice_index)); }
const ktx_image_data_vec& get_image_data_vec() const { return m_image_data; }
ktx_image_data_vec& get_image_data_vec() { return m_image_data; }
void add_image(uint face_index, uint mip_index, const void* pImage, uint image_size)
{
const uint image_index = get_image_index(mip_index, 0, face_index, 0);
if (image_index >= m_image_data.size())
m_image_data.resize(image_index + 1);
if (image_size)
{
uint8_vec& v = m_image_data[image_index];
v.resize(image_size);
memcpy(&v[0], pImage, image_size);
}
}
uint get_image_index(uint mip_index, uint array_index, uint face_index, uint zslice_index) const
{
CRNLIB_ASSERT((mip_index < get_num_mips()) && (array_index < get_array_size()) && (face_index < get_num_faces()) && (zslice_index < get_depth()));
return zslice_index + (face_index * get_depth()) + (array_index * (get_depth() * get_num_faces())) + (mip_index * (get_depth() * get_num_faces() * get_array_size()));
}
void get_mip_dim(uint mip_index, uint& mip_width, uint& mip_height) const
{
CRNLIB_ASSERT(mip_index < get_num_mips());
mip_width = CRNLIB_MAX(get_width() >> mip_index, 1);
mip_height = CRNLIB_MAX(get_height() >> mip_index, 1);
}
void get_mip_dim(uint mip_index, uint& mip_width, uint& mip_height, uint& mip_depth) const
{
CRNLIB_ASSERT(mip_index < get_num_mips());
mip_width = CRNLIB_MAX(get_width() >> mip_index, 1);
mip_height = CRNLIB_MAX(get_height() >> mip_index, 1);
mip_depth = CRNLIB_MAX(get_depth() >> mip_index, 1);
}
private:
ktx_header m_header;
ktx_key_value_vec m_key_values;
ktx_image_data_vec m_image_data;
uint32 m_block_dim;
uint32 m_bytes_per_block;
bool m_opposite_endianness;
bool compute_pixel_info();
};
} // namespace crnlib
#endif // #ifndef _KTX_TEXTURE_H_
+7
View File
@@ -216,10 +216,12 @@ namespace crnlib
void compute_lower_pow2_dim(int& width, int& height); void compute_lower_pow2_dim(int& width, int& height);
void compute_upper_pow2_dim(int& width, int& height); void compute_upper_pow2_dim(int& width, int& height);
inline bool equal_tol(float a, float b, float t) inline bool equal_tol(float a, float b, float t)
{ {
return fabs(a - b) < ((maximum(fabs(a), fabs(b)) + 1.0f) * t); return fabs(a - b) < ((maximum(fabs(a), fabs(b)) + 1.0f) * t);
} }
inline bool equal_tol(double a, double b, double t) inline bool equal_tol(double a, double b, double t)
{ {
return fabs(a - b) < ((maximum(fabs(a), fabs(b)) + 1.0f) * t); return fabs(a - b) < ((maximum(fabs(a), fabs(b)) + 1.0f) * t);
@@ -228,3 +230,8 @@ namespace crnlib
} // namespace crnlib } // namespace crnlib
+9
View File
@@ -72,6 +72,11 @@ namespace crnlib
p_new = ::malloc(size); p_new = ::malloc(size);
CRNLIB_ASSERT( (reinterpret_cast<ptr_bits_t>(p_new) & (CRNLIB_MIN_ALLOC_ALIGNMENT - 1)) == 0 ); CRNLIB_ASSERT( (reinterpret_cast<ptr_bits_t>(p_new) & (CRNLIB_MIN_ALLOC_ALIGNMENT - 1)) == 0 );
if (!p_new)
{
printf("WARNING: ::malloc() of size %u failed!\n", (uint)size);
}
if (pActual_size) if (pActual_size)
*pActual_size = p_new ? ::_msize(p_new) : 0; *pActual_size = p_new ? ::_msize(p_new) : 0;
} }
@@ -106,6 +111,10 @@ namespace crnlib
CRNLIB_ASSERT( (reinterpret_cast<ptr_bits_t>(p_new) & (CRNLIB_MIN_ALLOC_ALIGNMENT - 1)) == 0 ); CRNLIB_ASSERT( (reinterpret_cast<ptr_bits_t>(p_new) & (CRNLIB_MIN_ALLOC_ALIGNMENT - 1)) == 0 );
p_final_block = p_new; p_final_block = p_new;
} }
else
{
printf("WARNING: ::realloc() of size %u failed!\n", (uint)size);
}
} }
if (pActual_size) if (pActual_size)
+3948
View File
File diff suppressed because it is too large Load Diff
+893
View File
@@ -0,0 +1,893 @@
/* miniz.c v1.14 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing
See "unlicense" statement at the end of this file.
Rich Geldreich <richgel99@gmail.com>, last updated May 20, 2012
Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt
Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define
MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros).
* Change History
5/20/12 v1.14 - MinGW32/64 GCC 4.6.1 compiler fixes: added MZ_FORCEINLINE, #include <time.h> (thanks fermtect).
5/19/12 v1.13 - From jason@cornsyrup.org and kelwert@mtu.edu - Fix mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bit.
Temporarily/locally slammed in "typedef unsigned long mz_ulong" and re-ran a randomized regression test on ~500k files.
Eliminated a bunch of warnings when compiling with GCC 32-bit/64.
Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze (static analysis) option and fixed all warnings (except for the silly
"Use of the comma-operator in a tested expression.." analysis warning, which I purposely use to work around a MSVC compiler warning).
Created 32-bit and 64-bit Codeblocks projects/workspace. Built and tested Linux executables. The codeblocks workspace is compatible with Linux+Win32/x64.
Added miniz_tester solution/project, which is a useful little app derived from LZHAM's tester app that I use as part of the regression test.
Ran miniz.c and tinfl.c through another series of regression testing on ~500,000 files and archives.
Modified example5.c so it purposely disables a bunch of high-level functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the MINIZ_NO_STDIO bug report.)
Fix ftell() usage in examples so they exit with an error on files which are too large (a limitation of the examples, not miniz itself).
4/12/12 v1.12 - More comments, added low-level example5.c, fixed a couple minor level_and_flags issues in the archive API's.
level_and_flags can now be set to MZ_DEFAULT_COMPRESSION. Thanks to Bruce Dawson <bruced@valvesoftware.com> for the feedback/bug report.
5/28/11 v1.11 - Added statement from unlicense.org
5/27/11 v1.10 - Substantial compressor optimizations:
Level 1 is now ~4x faster than before. The L1 compressor's throughput now varies between 70-110MB/sec. on a
Core i7 (actual throughput varies depending on the type of data, and x64 vs. x86).
Improved baseline L2-L9 compression perf. Also, greatly improved compression perf. issues on some file types.
Refactored the compression code for better readability and maintainability.
Added level 10 compression level (L10 has slightly better ratio than level 9, but could have a potentially large
drop in throughput on some files).
5/15/11 v1.09 - Initial stable release.
* Low-level Deflate/Inflate implementation notes:
Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or
greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses
approximately as well as zlib.
Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function
coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory
block large enough to hold the entire file.
The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation.
* zlib-style API notes:
miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in
zlib replacement in many apps:
The z_stream struct, optional memory allocation callbacks
deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound
inflateInit/inflateInit2/inflate/inflateEnd
compress, compress2, compressBound, uncompress
CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines.
Supports raw deflate streams or standard zlib streams with adler-32 checking.
Limitations:
The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries.
I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but
there are no guarantees that miniz.c pulls this off perfectly.
* PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by
Alex Evans. Supports 1-4 bytes/pixel images.
* ZIP archive API notes:
The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to
get the job done with minimal fuss. There are simple API's to retrieve file information, read files from
existing archives, create new archives, append new files to existing archives, or clone archive data from
one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h),
or you can specify custom file read/write callbacks.
- Archive reading: Just call this function to read a single file from a disk archive:
void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name,
size_t *pSize, mz_uint zip_flags);
For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central
directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files.
- Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file:
int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags);
The locate operation can optionally check file comments too, which (as one example) can be used to identify
multiple versions of the same file in an archive. This function uses a simple linear search through the central
directory, so it's not very fast.
Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and
retrieve detailed info on each file by calling mz_zip_reader_file_stat().
- Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data
to disk and builds an exact image of the central directory in memory. The central directory image is written
all at once at the end of the archive file when the archive is finalized.
The archive writer can optionally align each file's local header and file data to any power of 2 alignment,
which can be useful when the archive will be read from optical media. Also, the writer supports placing
arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still
readable by any ZIP tool.
- Archive appending: The simple way to add a single file to an archive is to call this function:
mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name,
const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags);
The archive will be created if it doesn't already exist, otherwise it'll be appended to.
Note the appending is done in-place and is not an atomic operation, so if something goes wrong
during the operation it's possible the archive could be left without a central directory (although the local
file headers and file data will be fine, so the archive will be recoverable).
For more complex archive modification scenarios:
1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to
preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the
compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and
you're done. This is safe but requires a bunch of temporary disk space or heap memory.
2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(),
append new files as needed, then finalize the archive which will write an updated central directory to the
original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a
possibility that the archive's central directory could be lost with this method if anything goes wrong, though.
- ZIP archive support limitations:
No zip64 or spanning support. Extraction functions can only handle unencrypted, stored or deflated files.
Requires streams capable of seeking.
* This is a header file library, like stb_image.c. To get only a header file, either cut and paste the
below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it.
* Important: For best perf. be sure to customize the below macros for your target platform:
#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1
#define MINIZ_LITTLE_ENDIAN 1
#define MINIZ_HAS_64BIT_REGISTERS 1
*/
#pragma once
#ifndef MINIZ_HEADER_INCLUDED
#define MINIZ_HEADER_INCLUDED
#include <stdlib.h>
#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS)
#include <time.h>
#endif
// Defines to completely disable specific portions of miniz.c:
// If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl.
// Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O.
//#define MINIZ_NO_STDIO
// If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or
// get/set file times.
//#define MINIZ_NO_TIME
// Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's.
//#define MINIZ_NO_ARCHIVE_APIS
// Define MINIZ_NO_ARCHIVE_APIS to disable all writing related ZIP archive API's.
//#define MINIZ_NO_ARCHIVE_WRITING_APIS
// Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's.
//#define MINIZ_NO_ZLIB_APIS
// Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib.
//#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES
// Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc.
// Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc
// callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user
// functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work.
//#define MINIZ_NO_MALLOC
#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__)
// MINIZ_X86_OR_X64_CPU is only used to help set the below macros.
#define MINIZ_X86_OR_X64_CPU 1
#endif
#if (__BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
// Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian.
#define MINIZ_LITTLE_ENDIAN 1
#endif
#if MINIZ_X86_OR_X64_CPU
// Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses.
#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1
#endif
#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__)
// Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions).
#define MINIZ_HAS_64BIT_REGISTERS 1
#endif
#ifdef __cplusplus
extern "C" {
#endif
// ------------------- zlib-style API Definitions.
// For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits!
typedef unsigned long mz_ulong;
// mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap.
void mz_free(void *p);
#define MZ_ADLER32_INIT (1)
// mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL.
mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len);
#define MZ_CRC32_INIT (0)
// mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL.
mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len);
// Compression strategies.
enum { MZ_DEFAULT_STRATEGY = 0, MZ_FILTERED = 1, MZ_HUFFMAN_ONLY = 2, MZ_RLE = 3, MZ_FIXED = 4 };
// Method
#define MZ_DEFLATED 8
#ifndef MINIZ_NO_ZLIB_APIS
// Heap allocation callbacks.
// Note that mz_alloc_func parameter types purpsosely differ from zlib's: items/size is size_t, not unsigned long.
typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size);
typedef void (*mz_free_func)(void *opaque, void *address);
typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size);
#define MZ_VERSION "9.1.14"
#define MZ_VERNUM 0x91E0
#define MZ_VER_MAJOR 9
#define MZ_VER_MINOR 1
#define MZ_VER_REVISION 14
#define MZ_VER_SUBREVISION 0
// Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs).
enum { MZ_NO_FLUSH = 0, MZ_PARTIAL_FLUSH = 1, MZ_SYNC_FLUSH = 2, MZ_FULL_FLUSH = 3, MZ_FINISH = 4, MZ_BLOCK = 5 };
// Return status codes. MZ_PARAM_ERROR is non-standard.
enum { MZ_OK = 0, MZ_STREAM_END = 1, MZ_NEED_DICT = 2, MZ_ERRNO = -1, MZ_STREAM_ERROR = -2, MZ_DATA_ERROR = -3, MZ_MEM_ERROR = -4, MZ_BUF_ERROR = -5, MZ_VERSION_ERROR = -6, MZ_PARAM_ERROR = -10000 };
// Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL.
enum { MZ_NO_COMPRESSION = 0, MZ_BEST_SPEED = 1, MZ_BEST_COMPRESSION = 9, MZ_UBER_COMPRESSION = 10, MZ_DEFAULT_LEVEL = 6, MZ_DEFAULT_COMPRESSION = -1 };
// Window bits
#define MZ_DEFAULT_WINDOW_BITS 15
struct mz_internal_state;
// Compression/decompression stream struct.
typedef struct mz_stream_s
{
const unsigned char *next_in; // pointer to next byte to read
unsigned int avail_in; // number of bytes available at next_in
mz_ulong total_in; // total number of bytes consumed so far
unsigned char *next_out; // pointer to next byte to write
unsigned int avail_out; // number of bytes that can be written to next_out
mz_ulong total_out; // total number of bytes produced so far
char *msg; // error msg (unused)
struct mz_internal_state *state; // internal state, allocated by zalloc/zfree
mz_alloc_func zalloc; // optional heap allocation function (defaults to malloc)
mz_free_func zfree; // optional heap free function (defaults to free)
void *opaque; // heap alloc function user pointer
int data_type; // data_type (unused)
mz_ulong adler; // adler32 of the source or uncompressed data
mz_ulong reserved; // not used
} mz_stream;
typedef mz_stream *mz_streamp;
// Returns the version string of miniz.c.
const char *mz_version(void);
// mz_deflateInit() initializes a compressor with default options:
// Parameters:
// pStream must point to an initialized mz_stream struct.
// level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION].
// level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio.
// (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.)
// Return values:
// MZ_OK on success.
// MZ_STREAM_ERROR if the stream is bogus.
// MZ_PARAM_ERROR if the input parameters are bogus.
// MZ_MEM_ERROR on out of memory.
int mz_deflateInit(mz_streamp pStream, int level);
// mz_deflateInit2() is like mz_deflate(), except with more control:
// Additional parameters:
// method must be MZ_DEFLATED
// window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer)
// mem_level must be between [1, 9] (it's checked but ignored by miniz.c)
int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy);
// Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2().
int mz_deflateReset(mz_streamp pStream);
// mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible.
// Parameters:
// pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members.
// flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH.
// Return values:
// MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full).
// MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore.
// MZ_STREAM_ERROR if the stream is bogus.
// MZ_PARAM_ERROR if one of the parameters is invalid.
// MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.)
int mz_deflate(mz_streamp pStream, int flush);
// mz_deflateEnd() deinitializes a compressor:
// Return values:
// MZ_OK on success.
// MZ_STREAM_ERROR if the stream is bogus.
int mz_deflateEnd(mz_streamp pStream);
// mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH.
mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len);
// Single-call compression functions mz_compress() and mz_compress2():
// Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure.
int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len);
int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level);
// mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress().
mz_ulong mz_compressBound(mz_ulong source_len);
// Initializes a decompressor.
int mz_inflateInit(mz_streamp pStream);
// mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer:
// window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate).
int mz_inflateInit2(mz_streamp pStream, int window_bits);
// Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible.
// Parameters:
// pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members.
// flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH.
// On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster).
// MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data.
// Return values:
// MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full.
// MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified.
// MZ_STREAM_ERROR if the stream is bogus.
// MZ_DATA_ERROR if the deflate stream is invalid.
// MZ_PARAM_ERROR if one of the parameters is invalid.
// MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again
// with more input data, or with more room in the output buffer (except when using single call decompression, described above).
int mz_inflate(mz_streamp pStream, int flush);
// Deinitializes a decompressor.
int mz_inflateEnd(mz_streamp pStream);
// Single-call decompression.
// Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure.
int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len);
// Returns a string description of the specified error code, or NULL if the error code is invalid.
const char *mz_error(int err);
// Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports.
// Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project.
#ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES
typedef unsigned char Byte;
typedef unsigned int uInt;
typedef mz_ulong uLong;
typedef Byte Bytef;
typedef uInt uIntf;
typedef char charf;
typedef int intf;
typedef void *voidpf;
typedef uLong uLongf;
typedef void *voidp;
typedef void *const voidpc;
#define Z_NULL 0
#define Z_NO_FLUSH MZ_NO_FLUSH
#define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH
#define Z_SYNC_FLUSH MZ_SYNC_FLUSH
#define Z_FULL_FLUSH MZ_FULL_FLUSH
#define Z_FINISH MZ_FINISH
#define Z_BLOCK MZ_BLOCK
#define Z_OK MZ_OK
#define Z_STREAM_END MZ_STREAM_END
#define Z_NEED_DICT MZ_NEED_DICT
#define Z_ERRNO MZ_ERRNO
#define Z_STREAM_ERROR MZ_STREAM_ERROR
#define Z_DATA_ERROR MZ_DATA_ERROR
#define Z_MEM_ERROR MZ_MEM_ERROR
#define Z_BUF_ERROR MZ_BUF_ERROR
#define Z_VERSION_ERROR MZ_VERSION_ERROR
#define Z_PARAM_ERROR MZ_PARAM_ERROR
#define Z_NO_COMPRESSION MZ_NO_COMPRESSION
#define Z_BEST_SPEED MZ_BEST_SPEED
#define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION
#define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION
#define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY
#define Z_FILTERED MZ_FILTERED
#define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY
#define Z_RLE MZ_RLE
#define Z_FIXED MZ_FIXED
#define Z_DEFLATED MZ_DEFLATED
#define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS
#define alloc_func mz_alloc_func
#define free_func mz_free_func
#define internal_state mz_internal_state
#define z_stream mz_stream
#define deflateInit mz_deflateInit
#define deflateInit2 mz_deflateInit2
#define deflateReset mz_deflateReset
#define deflate mz_deflate
#define deflateEnd mz_deflateEnd
#define deflateBound mz_deflateBound
#define compress mz_compress
#define compress2 mz_compress2
#define compressBound mz_compressBound
#define inflateInit mz_inflateInit
#define inflateInit2 mz_inflateInit2
#define inflate mz_inflate
#define inflateEnd mz_inflateEnd
#define uncompress mz_uncompress
#define crc32 mz_crc32
#define adler32 mz_adler32
#define MAX_WBITS 15
#define MAX_MEM_LEVEL 9
#define zError mz_error
#define ZLIB_VERSION MZ_VERSION
#define ZLIB_VERNUM MZ_VERNUM
#define ZLIB_VER_MAJOR MZ_VER_MAJOR
#define ZLIB_VER_MINOR MZ_VER_MINOR
#define ZLIB_VER_REVISION MZ_VER_REVISION
#define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION
#define zlibVersion mz_version
#define zlib_version mz_version()
#endif // #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES
#endif // MINIZ_NO_ZLIB_APIS
// ------------------- Types and macros
typedef unsigned char mz_uint8;
typedef signed short mz_int16;
typedef unsigned short mz_uint16;
typedef unsigned int mz_uint32;
typedef unsigned int mz_uint;
typedef long long mz_int64;
typedef unsigned long long mz_uint64;
typedef int mz_bool;
#define MZ_FALSE (0)
#define MZ_TRUE (1)
// Works around MSVC's spammy "warning C4127: conditional expression is constant" message.
#ifdef _MSC_VER
#define MZ_MACRO_END while (0, 0)
#else
#define MZ_MACRO_END while (0)
#endif
// ------------------- ZIP archive reading/writing
#ifndef MINIZ_NO_ARCHIVE_APIS
enum
{
MZ_ZIP_MAX_IO_BUF_SIZE = 64*1024,
MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 260,
MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 256
};
typedef struct
{
mz_uint32 m_file_index;
mz_uint32 m_central_dir_ofs;
mz_uint16 m_version_made_by;
mz_uint16 m_version_needed;
mz_uint16 m_bit_flag;
mz_uint16 m_method;
#ifndef MINIZ_NO_TIME
time_t m_time;
#endif
mz_uint32 m_crc32;
mz_uint64 m_comp_size;
mz_uint64 m_uncomp_size;
mz_uint16 m_internal_attr;
mz_uint32 m_external_attr;
mz_uint64 m_local_header_ofs;
mz_uint32 m_comment_size;
char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE];
char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE];
} mz_zip_archive_file_stat;
typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n);
typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n);
struct mz_zip_internal_state_tag;
typedef struct mz_zip_internal_state_tag mz_zip_internal_state;
typedef enum
{
MZ_ZIP_MODE_INVALID = 0,
MZ_ZIP_MODE_READING = 1,
MZ_ZIP_MODE_WRITING = 2,
MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3
} mz_zip_mode;
typedef struct
{
mz_uint64 m_archive_size;
mz_uint64 m_central_directory_file_ofs;
mz_uint m_total_files;
mz_zip_mode m_zip_mode;
mz_uint m_file_offset_alignment;
mz_alloc_func m_pAlloc;
mz_free_func m_pFree;
mz_realloc_func m_pRealloc;
void *m_pAlloc_opaque;
mz_file_read_func m_pRead;
mz_file_write_func m_pWrite;
void *m_pIO_opaque;
mz_zip_internal_state *m_pState;
} mz_zip_archive;
typedef enum
{
MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100,
MZ_ZIP_FLAG_IGNORE_PATH = 0x0200,
MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400,
MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800
} mz_zip_flags;
// ZIP archive reading
// Inits a ZIP archive reader.
// These functions read and validate the archive's central directory.
mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags);
mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags);
#ifndef MINIZ_NO_STDIO
mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags);
#endif
// Returns the total number of files in the archive.
mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip);
// Returns detailed information about an archive file entry.
mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat);
// Determines if an archive file entry is a directory entry.
mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index);
mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index);
// Retrieves the filename of an archive file entry.
// Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename.
mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size);
// Attempts to locates a file in the archive's central directory.
// Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH
// Returns -1 if the file cannot be found.
int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags);
// Extracts a archive file to a memory buffer using no memory allocation.
mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size);
mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size);
// Extracts a archive file to a memory buffer.
mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags);
mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags);
// Extracts a archive file to a dynamically allocated heap buffer.
void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags);
void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags);
// Extracts a archive file using a callback function to output the file's data.
mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags);
mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags);
#ifndef MINIZ_NO_STDIO
// Extracts a archive file to a disk file and sets its last accessed and modified times.
// This function only extracts files, not archive directory records.
mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags);
mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags);
#endif
// Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used.
mz_bool mz_zip_reader_end(mz_zip_archive *pZip);
// ZIP archive writing
#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS
// Inits a ZIP archive writer.
mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size);
mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size);
#ifndef MINIZ_NO_STDIO
mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning);
#endif
// Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive.
// For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called.
// For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it).
// Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL.
// Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before
// the archive is finalized the file's central directory will be hosed.
mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename);
// Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive.
// To add a directory entry, call this method with an archive name ending in a forwardslash with empty buffer.
// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION.
mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags);
mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32);
#ifndef MINIZ_NO_STDIO
// Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive.
// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION.
mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags);
#endif
// Adds a file to an archive by fully cloning the data from another archive.
// This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data, and comment fields.
mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index);
// Finalizes the archive by writing the central directory records followed by the end of central directory record.
// After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end().
// An archive must be manually finalized by calling this function for it to be valid.
mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip);
mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize);
// Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used.
// Note for the archive to be valid, it must have been finalized before ending.
mz_bool mz_zip_writer_end(mz_zip_archive *pZip);
// Misc. high-level helper functions:
// mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive.
// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION.
mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags);
// Reads a single file from an archive into a heap block.
// If pComment is not NULL, only the file with the specified comment will be extracted.
// Returns NULL on failure.
void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags);
#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS
#endif // #ifndef MINIZ_NO_ARCHIVE_APIS
// ------------------- Low-level Decompression API Definitions
// Decompression flags used by tinfl_decompress().
// TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream.
// TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input.
// TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB).
// TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes.
enum
{
TINFL_FLAG_PARSE_ZLIB_HEADER = 1,
TINFL_FLAG_HAS_MORE_INPUT = 2,
TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4,
TINFL_FLAG_COMPUTE_ADLER32 = 8
};
// High level decompression functions:
// tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc().
// On entry:
// pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress.
// On return:
// Function returns a pointer to the decompressed data, or NULL on failure.
// *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data.
// The caller must call mz_free() on the returned block when it's no longer needed.
void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags);
// tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory.
// Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success.
#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1))
size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags);
// tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer.
// Returns 1 on success or 0 on failure.
typedef int (*tinfl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser);
int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);
struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor;
// Max size of LZ dictionary.
#define TINFL_LZ_DICT_SIZE 32768
// Return status.
typedef enum
{
TINFL_STATUS_BAD_PARAM = -3,
TINFL_STATUS_ADLER32_MISMATCH = -2,
TINFL_STATUS_FAILED = -1,
TINFL_STATUS_DONE = 0,
TINFL_STATUS_NEEDS_MORE_INPUT = 1,
TINFL_STATUS_HAS_MORE_OUTPUT = 2
} tinfl_status;
// Initializes the decompressor to its initial state.
#define tinfl_init(r) do { (r)->m_state = 0; } MZ_MACRO_END
#define tinfl_get_adler32(r) (r)->m_check_adler32
// Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability.
// This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output.
tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags);
// Internal/private bits follow.
enum
{
TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19,
TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS
};
typedef struct
{
mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0];
mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2];
} tinfl_huff_table;
#if MINIZ_HAS_64BIT_REGISTERS
#define TINFL_USE_64BIT_BITBUF 1
#endif
#if TINFL_USE_64BIT_BITBUF
typedef mz_uint64 tinfl_bit_buf_t;
#define TINFL_BITBUF_SIZE (64)
#else
typedef mz_uint32 tinfl_bit_buf_t;
#define TINFL_BITBUF_SIZE (32)
#endif
struct tinfl_decompressor_tag
{
mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES];
tinfl_bit_buf_t m_bit_buf;
size_t m_dist_from_out_buf_start;
tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES];
mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137];
};
// ------------------- Low-level Compression API Definitions
// Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently).
#define TDEFL_LESS_MEMORY 0
// tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search):
// TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression).
enum
{
TDEFL_HUFFMAN_ONLY = 0, TDEFL_DEFAULT_MAX_PROBES = 128, TDEFL_MAX_PROBES_MASK = 0xFFF
};
// TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data.
// TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers).
// TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing.
// TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory).
// TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1)
// TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled.
// TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables.
// TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks.
enum
{
TDEFL_WRITE_ZLIB_HEADER = 0x01000,
TDEFL_COMPUTE_ADLER32 = 0x02000,
TDEFL_GREEDY_PARSING_FLAG = 0x04000,
TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000,
TDEFL_RLE_MATCHES = 0x10000,
TDEFL_FILTER_MATCHES = 0x20000,
TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000,
TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000
};
// High level compression functions:
// tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc().
// On entry:
// pSrc_buf, src_buf_len: Pointer and size of source block to compress.
// flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression.
// On return:
// Function returns a pointer to the compressed data, or NULL on failure.
// *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data.
// The caller must free() the returned block when it's no longer needed.
void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags);
// tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory.
// Returns 0 on failure.
size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags);
// Compresses an image to a compressed PNG file in memory.
// On entry:
// pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4.
// The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory.
// On return:
// Function returns a pointer to the compressed data, or NULL on failure.
// *pLen_out will be set to the size of the PNG image file.
// The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed.
void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out);
// Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time.
typedef mz_bool (*tdefl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser);
// tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally.
mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);
enum { TDEFL_MAX_HUFF_TABLES = 3, TDEFL_MAX_HUFF_SYMBOLS_0 = 288, TDEFL_MAX_HUFF_SYMBOLS_1 = 32, TDEFL_MAX_HUFF_SYMBOLS_2 = 19, TDEFL_LZ_DICT_SIZE = 32768, TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, TDEFL_MIN_MATCH_LEN = 3, TDEFL_MAX_MATCH_LEN = 258 };
// TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes).
#if TDEFL_LESS_MEMORY
enum { TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 12, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS };
#else
enum { TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 15, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS };
#endif
// The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions.
typedef enum
{
TDEFL_STATUS_BAD_PARAM = -2,
TDEFL_STATUS_PUT_BUF_FAILED = -1,
TDEFL_STATUS_OKAY = 0,
TDEFL_STATUS_DONE = 1,
} tdefl_status;
// Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums
typedef enum
{
TDEFL_NO_FLUSH = 0,
TDEFL_SYNC_FLUSH = 2,
TDEFL_FULL_FLUSH = 3,
TDEFL_FINISH = 4
} tdefl_flush;
// tdefl's compression state structure.
typedef struct
{
tdefl_put_buf_func_ptr m_pPut_buf_func;
void *m_pPut_buf_user;
mz_uint m_flags, m_max_probes[2];
int m_greedy_parsing;
mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size;
mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end;
mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer;
mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish;
tdefl_status m_prev_return_status;
const void *m_pIn_buf;
void *m_pOut_buf;
size_t *m_pIn_buf_size, *m_pOut_buf_size;
tdefl_flush m_flush;
const mz_uint8 *m_pSrc;
size_t m_src_buf_left, m_out_buf_ofs;
mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1];
mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS];
mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS];
mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS];
mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE];
mz_uint16 m_next[TDEFL_LZ_DICT_SIZE];
mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE];
mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE];
} tdefl_compressor;
// Initializes the compressor.
// There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory.
// pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression.
// If pBut_buf_func is NULL the user should always call the tdefl_compress() API.
// flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.)
tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);
// Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible.
tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush);
// tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr.
// tdefl_compress_buffer() always consumes the entire input buffer.
tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush);
tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d);
mz_uint32 tdefl_get_adler32(tdefl_compressor *d);
// Can't use tdefl_create_comp_flags_from_zip_params if MINIZ_NO_ZLIB_APIS isn't defined, because it uses some of its macros.
#ifndef MINIZ_NO_ZLIB_APIS
// Create tdefl_compress() flags given zlib-style compression parameters.
// level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files)
// window_bits may be -15 (raw deflate) or 15 (zlib)
// strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED
mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy);
#endif // #ifndef MINIZ_NO_ZLIB_APIS
#ifdef __cplusplus
}
#endif
#endif // MINIZ_HEADER_INCLUDED
File diff suppressed because it is too large Load Diff
@@ -1,4 +1,4 @@
// File: crn_dds_texture.h // File: crn_mipmapped_texture.h
// See Copyright Notice and license at the end of inc/crnlib.h // See Copyright Notice and license at the end of inc/crnlib.h
#pragma once #pragma once
#include "crn_dxt_image.h" #include "crn_dxt_image.h"
@@ -16,9 +16,23 @@ namespace crnlib
{ {
extern const vec2I g_vertical_cross_image_offsets[6]; extern const vec2I g_vertical_cross_image_offsets[6];
enum orientation_flags_t
{
cOrientationFlagXFlipped = 1,
cOrientationFlagYFlipped = 2,
cDefaultOrientationFlags = 0
};
enum unpack_flags_t
{
cUnpackFlagUncook = 1,
cUnpackFlagUnflip = 2
};
class mip_level class mip_level
{ {
friend class dds_texture; friend class mipmapped_texture;
public: public:
mip_level(); mip_level();
@@ -28,19 +42,22 @@ namespace crnlib
mip_level& operator= (const mip_level& rhs); mip_level& operator= (const mip_level& rhs);
// Assumes ownership. // Assumes ownership.
void assign(image_u8* p, pixel_format fmt = PIXEL_FMT_INVALID); void assign(image_u8* p, pixel_format fmt = PIXEL_FMT_INVALID, orientation_flags_t orient_flags = cDefaultOrientationFlags);
void assign(dxt_image* p, pixel_format fmt = PIXEL_FMT_INVALID); void assign(dxt_image* p, pixel_format fmt = PIXEL_FMT_INVALID, orientation_flags_t orient_flags = cDefaultOrientationFlags);
void clear(); void clear();
inline uint get_width() const { return m_width; } inline uint get_width() const { return m_width; }
inline uint get_height() const { return m_height; } inline uint get_height() const { return m_height; }
inline uint get_total_pixels() const { return m_width * m_height; } inline uint get_total_pixels() const { return m_width * m_height; }
orientation_flags_t get_orientation_flags() const { return m_orient_flags; }
void set_orientation_flags(orientation_flags_t flags) { m_orient_flags = flags; }
inline image_u8* get_image() const { return m_pImage; } inline image_u8* get_image() const { return m_pImage; }
inline dxt_image* get_dxt_image() const { return m_pDXTImage; } inline dxt_image* get_dxt_image() const { return m_pDXTImage; }
image_u8* get_unpacked_image(image_u8& tmp, bool uncook) const; image_u8* get_unpacked_image(image_u8& tmp, uint unpack_flags) const;
inline bool is_packed() const { return m_pDXTImage != NULL; } inline bool is_packed() const { return m_pDXTImage != NULL; }
@@ -54,14 +71,29 @@ namespace crnlib
bool convert(pixel_format fmt, bool cook, const dxt_image::pack_params& p); bool convert(pixel_format fmt, bool cook, const dxt_image::pack_params& p);
bool pack_to_dxt(const image_u8& img, pixel_format fmt, bool cook, const dxt_image::pack_params& p); bool pack_to_dxt(const image_u8& img, pixel_format fmt, bool cook, const dxt_image::pack_params& p, orientation_flags_t orient_flags = cDefaultOrientationFlags);
bool pack_to_dxt(pixel_format fmt, bool cook, const dxt_image::pack_params& p); bool pack_to_dxt(pixel_format fmt, bool cook, const dxt_image::pack_params& p);
bool unpack_from_dxt(bool uncook = true); bool unpack_from_dxt(bool uncook = true);
// Returns true if flipped on either axis.
bool is_flipped() const;
bool is_x_flipped() const;
bool is_y_flipped() const;
bool can_unflip_without_unpacking() const;
// Returns true if unflipped on either axis.
// Will try to flip packed (DXT/ETC) data in-place, if this isn't possible it'll unpack/uncook the mip level then unflip.
bool unflip(bool allow_unpacking_to_flip, bool uncook_during_unpack);
bool set_alpha_to_luma(); bool set_alpha_to_luma();
bool convert(image_utils::conversion_type conv_type); bool convert(image_utils::conversion_type conv_type);
bool flip_x();
bool flip_y();
private: private:
uint m_width; uint m_width;
uint m_height; uint m_height;
@@ -72,6 +104,8 @@ namespace crnlib
image_u8* m_pImage; image_u8* m_pImage;
dxt_image* m_pDXTImage; dxt_image* m_pDXTImage;
orientation_flags_t m_orient_flags;
void cook_image(image_u8& img) const; void cook_image(image_u8& img) const;
void uncook_image(image_u8& img) const; void uncook_image(image_u8& img) const;
}; };
@@ -82,30 +116,30 @@ namespace crnlib
// And an array of one, six, or N faces make up a texture. // And an array of one, six, or N faces make up a texture.
typedef crnlib::vector<mip_ptr_vec> face_vec; typedef crnlib::vector<mip_ptr_vec> face_vec;
class dds_texture class mipmapped_texture
{ {
public: public:
// Construction/destruction // Construction/destruction
dds_texture(); mipmapped_texture();
~dds_texture(); ~mipmapped_texture();
dds_texture(const dds_texture& other); mipmapped_texture(const mipmapped_texture& other);
dds_texture& operator= (const dds_texture& rhs); mipmapped_texture& operator= (const mipmapped_texture& rhs);
void clear(); void clear();
void init(uint width, uint height, uint levels, uint faces, pixel_format fmt, const char* pName); void init(uint width, uint height, uint levels, uint faces, pixel_format fmt, const char* pName, orientation_flags_t orient_flags);
// Assumes ownership. // Assumes ownership.
void assign(face_vec& faces); void assign(face_vec& faces);
void assign(mip_level* pLevel); void assign(mip_level* pLevel);
void assign(image_u8* p, pixel_format fmt = PIXEL_FMT_INVALID); void assign(image_u8* p, pixel_format fmt = PIXEL_FMT_INVALID, orientation_flags_t orient_flags = cDefaultOrientationFlags);
void assign(dxt_image* p, pixel_format fmt = PIXEL_FMT_INVALID); void assign(dxt_image* p, pixel_format fmt = PIXEL_FMT_INVALID, orientation_flags_t orient_flags = cDefaultOrientationFlags);
void set(texture_file_types::format source_file_type, const dds_texture& dds_texture); void set(texture_file_types::format source_file_type, const mipmapped_texture& mipmapped_texture);
// Accessors // Accessors
image_u8* get_level_image(uint face, uint level, image_u8& img, bool uncook = true) const; image_u8* get_level_image(uint face, uint level, image_u8& img, uint unpack_flags = cUnpackFlagUncook | cUnpackFlagUnflip) const;
inline bool is_valid() const { return m_faces.size() > 0; } inline bool is_valid() const { return m_faces.size() > 0; }
@@ -143,23 +177,26 @@ namespace crnlib
const dynamic_string& get_last_error() const { return m_last_error; } const dynamic_string& get_last_error() const { return m_last_error; }
void clear_last_error() { m_last_error.clear(); } void clear_last_error() { m_last_error.clear(); }
// Loading/saving // Reading/writing
bool read_dds(const char* pFilename);
bool read_dds(data_stream_serializer& serializer); bool read_dds(data_stream_serializer& serializer);
bool write_dds(const char* pFilename) const;
bool write_dds(data_stream_serializer& serializer) const; bool write_dds(data_stream_serializer& serializer) const;
bool load_crn_from_memory(const char* pFilename, const void *pData, uint data_size); bool read_ktx(data_stream_serializer& serializer);
bool write_ktx(data_stream_serializer& serializer) const;
bool read_crn(data_stream_serializer& serializer);
bool read_crn_from_memory(const void *pData, uint data_size, const char* pFilename);
// If file_format is texture_file_types::cFormatInvalid, the format will be determined from the filename's extension. // If file_format is texture_file_types::cFormatInvalid, the format will be determined from the filename's extension.
bool load_from_file(const char* pFilename, texture_file_types::format file_format); bool read_from_file(const char* pFilename, texture_file_types::format file_format = texture_file_types::cFormatInvalid);
bool read_from_stream(data_stream_serializer& serializer, texture_file_types::format file_format = texture_file_types::cFormatInvalid);
bool write_to_file( bool write_to_file(
const char* pFilename, const char* pFilename,
texture_file_types::format file_format, texture_file_types::format file_format = texture_file_types::cFormatInvalid,
crn_comp_params* pCRN_comp_params, crn_comp_params* pComp_params = NULL,
uint32 *pActual_quality_level, float *pActual_bitrate); uint32* pActual_quality_level = NULL, float* pActual_bitrate = NULL,
uint32 image_write_flags = 0);
// Conversion // Conversion
bool convert(pixel_format fmt, bool cook, const dxt_image::pack_params& p); bool convert(pixel_format fmt, bool cook, const dxt_image::pack_params& p);
@@ -248,15 +285,26 @@ namespace crnlib
utils::zero_object(m_has_blocks); utils::zero_object(m_has_blocks);
} }
}; };
bool qdxt_pack_init(qdxt_state& state, dds_texture& dst_tex, const qdxt1_params& dxt1_params, const qdxt5_params& dxt5_params, pixel_format fmt, bool cook); bool qdxt_pack_init(qdxt_state& state, mipmapped_texture& dst_tex, const qdxt1_params& dxt1_params, const qdxt5_params& dxt5_params, pixel_format fmt, bool cook);
bool qdxt_pack(qdxt_state& state, dds_texture& dst_tex, const qdxt1_params& dxt1_params, const qdxt5_params& dxt5_params); bool qdxt_pack(qdxt_state& state, mipmapped_texture& dst_tex, const qdxt1_params& dxt1_params, const qdxt5_params& dxt5_params);
void swap(dds_texture& img); void swap(mipmapped_texture& img);
bool check() const; bool check() const;
void set_orientation_flags(orientation_flags_t flags);
// Returns true if any face/miplevel is flipped.
bool is_flipped() const;
bool is_x_flipped() const;
bool is_y_flipped() const;
bool can_unflip_without_unpacking() const;
bool unflip(bool allow_unpacking_to_flip, bool uncook_if_necessary_to_unpack);
bool flip_y(bool update_orientation_flags);
private: private:
dynamic_string m_name; dynamic_string m_name;
uint m_width; uint m_width;
uint m_height; uint m_height;
@@ -268,23 +316,22 @@ namespace crnlib
texture_file_types::format m_source_file_type; texture_file_types::format m_source_file_type;
mutable dynamic_string m_last_error; mutable dynamic_string m_last_error;
inline void clear_last_error() const { m_last_error.clear(); } inline void clear_last_error() const { m_last_error.clear(); }
inline void set_last_error(const char* p) const { m_last_error = p; } inline void set_last_error(const char* p) const { m_last_error = p; }
void free_all_mips(); void free_all_mips();
bool read_regular_image(data_stream_serializer &serializer, texture_file_types::format file_format);
bool write_regular_image(const char* pFilename, uint32 image_write_flags);
bool read_dds_internal(data_stream_serializer& serializer); bool read_dds_internal(data_stream_serializer& serializer);
bool load_regular(const char* pFilename, texture_file_types::format file_format);
bool load_dds(const char* pFilename);
bool load_crn(const char* pFilename);
void print_crn_comp_params(const crn_comp_params& p); void print_crn_comp_params(const crn_comp_params& p);
bool save_regular(const char* pFilename); bool write_comp_texture(const char* pFilename, const crn_comp_params &comp_params, uint32 *pActual_quality_level, float *pActual_bitrate);
bool save_dds(const char* pFilename); void change_dxt1_to_dxt1a();
bool save_comp_texture(const char* pFilename, const crn_comp_params &comp_params, uint32 *pActual_quality_level, float *pActual_bitrate); bool flip_y_helper();
}; };
inline void swap(dds_texture& a, dds_texture& b) inline void swap(mipmapped_texture& a, mipmapped_texture& b)
{ {
a.swap(b); a.swap(b);
} }
+13 -1
View File
@@ -23,6 +23,7 @@ namespace crnlib
PIXEL_FMT_DXT5_xGBR, PIXEL_FMT_DXT5_xGBR,
PIXEL_FMT_DXT5_AGBR, PIXEL_FMT_DXT5_AGBR,
PIXEL_FMT_DXT1A, PIXEL_FMT_DXT1A,
PIXEL_FMT_ETC1,
PIXEL_FMT_R8G8B8, PIXEL_FMT_R8G8B8,
PIXEL_FMT_L8, PIXEL_FMT_L8,
PIXEL_FMT_A8, PIXEL_FMT_A8,
@@ -59,6 +60,7 @@ namespace crnlib
case PIXEL_FMT_DXT5_xGxR: return "DXT5_xGxR"; case PIXEL_FMT_DXT5_xGxR: return "DXT5_xGxR";
case PIXEL_FMT_DXT5_xGBR: return "DXT5_xGBR"; case PIXEL_FMT_DXT5_xGBR: return "DXT5_xGBR";
case PIXEL_FMT_DXT5_AGBR: return "DXT5_AGBR"; case PIXEL_FMT_DXT5_AGBR: return "DXT5_AGBR";
case PIXEL_FMT_ETC1: return "ETC1";
case PIXEL_FMT_R8G8B8: return "R8G8B8"; case PIXEL_FMT_R8G8B8: return "R8G8B8";
case PIXEL_FMT_A8R8G8B8: return "A8R8G8B8"; case PIXEL_FMT_A8R8G8B8: return "A8R8G8B8";
case PIXEL_FMT_A8: return "A8"; case PIXEL_FMT_A8: return "A8";
@@ -84,6 +86,7 @@ namespace crnlib
case cCRNFmtDXN_XY: return "DXN_XY"; case cCRNFmtDXN_XY: return "DXN_XY";
case cCRNFmtDXN_YX: return "DXN_YX"; case cCRNFmtDXN_YX: return "DXN_YX";
case cCRNFmtDXT5A: return "DXT5A"; case cCRNFmtDXT5A: return "DXT5A";
case cCRNFmtETC1: return "ETC1";
default: break; default: break;
} }
CRNLIB_ASSERT(false); CRNLIB_ASSERT(false);
@@ -98,6 +101,7 @@ namespace crnlib
switch (fmt) switch (fmt)
{ {
case PIXEL_FMT_DXT1: case PIXEL_FMT_DXT1:
case PIXEL_FMT_ETC1:
{ {
flags = cCompFlagRValid | cCompFlagGValid | cCompFlagBValid; flags = cCompFlagRValid | cCompFlagGValid | cCompFlagBValid;
break; break;
@@ -233,6 +237,9 @@ namespace crnlib
case PIXEL_FMT_DXT5_xGxR: case PIXEL_FMT_DXT5_xGxR:
fmt = cCRNFmtDXT5_xGxR; fmt = cCRNFmtDXT5_xGxR;
break; break;
case PIXEL_FMT_ETC1:
fmt = cCRNFmtETC1;
break;
default: default:
{ {
CRNLIB_ASSERT(false); CRNLIB_ASSERT(false);
@@ -256,7 +263,12 @@ namespace crnlib
case cCRNFmtDXN_XY: return PIXEL_FMT_DXN; case cCRNFmtDXN_XY: return PIXEL_FMT_DXN;
case cCRNFmtDXN_YX: return PIXEL_FMT_3DC; case cCRNFmtDXN_YX: return PIXEL_FMT_3DC;
case cCRNFmtDXT5A: return PIXEL_FMT_DXT5A; case cCRNFmtDXT5A: return PIXEL_FMT_DXT5A;
default: break; case cCRNFmtETC1: return PIXEL_FMT_ETC1;
default:
{
CRNLIB_ASSERT(false);
break;
}
} }
return PIXEL_FMT_INVALID; return PIXEL_FMT_INVALID;
+6 -1
View File
@@ -99,6 +99,7 @@ namespace crnlib
case PIXEL_FMT_DXT5_xGxR: case PIXEL_FMT_DXT5_xGxR:
case PIXEL_FMT_DXT5_xGBR: case PIXEL_FMT_DXT5_xGBR:
case PIXEL_FMT_DXT5_AGBR: case PIXEL_FMT_DXT5_AGBR:
case PIXEL_FMT_ETC1:
return true; return true;
default: break; default: break;
} }
@@ -138,6 +139,7 @@ namespace crnlib
case PIXEL_FMT_DXT5_xGxR: return cDXT5; case PIXEL_FMT_DXT5_xGxR: return cDXT5;
case PIXEL_FMT_DXT5_xGBR: return cDXT5; case PIXEL_FMT_DXT5_xGBR: return cDXT5;
case PIXEL_FMT_DXT5_AGBR: return cDXT5; case PIXEL_FMT_DXT5_AGBR: return cDXT5;
case PIXEL_FMT_ETC1: return cETC1;
default: break; default: break;
} }
return cDXTInvalid; return cDXTInvalid;
@@ -161,6 +163,8 @@ namespace crnlib
return PIXEL_FMT_3DC; return PIXEL_FMT_3DC;
case cDXT5A: case cDXT5A:
return PIXEL_FMT_DXT5A; return PIXEL_FMT_DXT5A;
case cETC1:
return PIXEL_FMT_ETC1;
default: break; default: break;
} }
CRNLIB_ASSERT(false); CRNLIB_ASSERT(false);
@@ -207,6 +211,7 @@ namespace crnlib
{ {
case PIXEL_FMT_DXT1: return 4; case PIXEL_FMT_DXT1: return 4;
case PIXEL_FMT_DXT1A: return 4; case PIXEL_FMT_DXT1A: return 4;
case PIXEL_FMT_ETC1: return 4;
case PIXEL_FMT_DXT2: return 8; case PIXEL_FMT_DXT2: return 8;
case PIXEL_FMT_DXT3: return 8; case PIXEL_FMT_DXT3: return 8;
case PIXEL_FMT_DXT4: return 8; case PIXEL_FMT_DXT4: return 8;
@@ -236,7 +241,7 @@ namespace crnlib
case PIXEL_FMT_DXT1: return 8; case PIXEL_FMT_DXT1: return 8;
case PIXEL_FMT_DXT1A: return 8; case PIXEL_FMT_DXT1A: return 8;
case PIXEL_FMT_DXT5A: return 8; case PIXEL_FMT_DXT5A: return 8;
case PIXEL_FMT_ETC1: return 8;
case PIXEL_FMT_DXT2: return 16; case PIXEL_FMT_DXT2: return 16;
case PIXEL_FMT_DXT3: return 16; case PIXEL_FMT_DXT3: return 16;
case PIXEL_FMT_DXT4: return 16; case PIXEL_FMT_DXT4: return 16;
+3
View File
@@ -45,10 +45,13 @@ const bool c_crnlib_big_endian_platform = !c_crnlib_little_endian_platform;
#if defined(__GNUC__) #if defined(__GNUC__)
#define CRNLIB_ALIGNED(x) __attribute__((aligned(x))) #define CRNLIB_ALIGNED(x) __attribute__((aligned(x)))
#define CRNLIB_NOINLINE __attribute__((noinline))
#elif defined(_MSC_VER) #elif defined(_MSC_VER)
#define CRNLIB_ALIGNED(x) __declspec(align(x)) #define CRNLIB_ALIGNED(x) __declspec(align(x))
#define CRNLIB_NOINLINE __declspec(noinline)
#else #else
#define CRNLIB_ALIGNED(x) #define CRNLIB_ALIGNED(x)
#define CRNLIB_NOINLINE
#endif #endif
#define CRNLIB_GET_ALIGNMENT(v) ((!sizeof(v)) ? 1 : (__alignof(v) ? __alignof(v) : sizeof(uint32))) #define CRNLIB_GET_ALIGNMENT(v) ((!sizeof(v)) ? 1 : (__alignof(v) ? __alignof(v) : sizeof(uint32)))
+1 -1
View File
@@ -306,7 +306,7 @@ namespace crnlib
#if GENERATE_DEBUG_IMAGES #if GENERATE_DEBUG_IMAGES
if (debugging) if (debugging)
image_utils::save_to_file_stb(dynamic_string(cVarArg, "debug_%u.tga", level).get_ptr(), debug_img, image_utils::cSaveIgnoreAlpha); image_utils::write_to_file(dynamic_string(cVarArg, "debug_%u.tga", level).get_ptr(), debug_img, image_utils::cWriteFlagIgnoreAlpha);
#endif #endif
} // level } // level
+1 -1
View File
@@ -286,7 +286,7 @@ namespace crnlib
#if QDXT5_DEBUGGING #if QDXT5_DEBUGGING
if (debugging) if (debugging)
image_utils::save_to_file_stb(dynamic_wstring(cVarArg, "debug_%u.tga", level).get_ptr(), debug_img, image_utils::cSaveIgnoreAlpha); image_utils::write_to_file(dynamic_wstring(cVarArg, "debug_%u.tga", level).get_ptr(), debug_img, image_utils::cWriteFlagIgnoreAlpha);
#endif #endif
} // level } // level
+345
View File
@@ -0,0 +1,345 @@
// File: crn_radix_sort.h
// See Copyright Notice and license at the end of inc/crnlib.h
#pragma once
namespace crnlib
{
// Returns pointer to sorted array.
template<typename T>
T* radix_sort(uint num_vals, T* pBuf0, T* pBuf1, uint key_ofs, uint key_size)
{
CRNLIB_ASSERT_OPEN_RANGE(key_ofs, 0, sizeof(T));
CRNLIB_ASSERT_CLOSED_RANGE(key_size, 1, 4);
uint hist[256 * 4];
memset(hist, 0, sizeof(hist[0]) * 256 * key_size);
#define CRNLIB_GET_KEY(p) (*(uint*)((uint8*)(p) + key_ofs))
if (key_size == 4)
{
T* p = pBuf0;
T* q = pBuf0 + num_vals;
for ( ; p != q; p++)
{
const uint key = CRNLIB_GET_KEY(p);
hist[ key & 0xFF]++;
hist[256 + ((key >> 8) & 0xFF)]++;
hist[512 + ((key >> 16) & 0xFF)]++;
hist[768 + ((key >> 24) & 0xFF)]++;
}
}
else if (key_size == 3)
{
T* p = pBuf0;
T* q = pBuf0 + num_vals;
for ( ; p != q; p++)
{
const uint key = CRNLIB_GET_KEY(p);
hist[ key & 0xFF]++;
hist[256 + ((key >> 8) & 0xFF)]++;
hist[512 + ((key >> 16) & 0xFF)]++;
}
}
else if (key_size == 2)
{
T* p = pBuf0;
T* q = pBuf0 + (num_vals >> 1) * 2;
for ( ; p != q; p += 2)
{
const uint key0 = CRNLIB_GET_KEY(p);
const uint key1 = CRNLIB_GET_KEY(p+1);
hist[ key0 & 0xFF]++;
hist[256 + ((key0 >> 8) & 0xFF)]++;
hist[ key1 & 0xFF]++;
hist[256 + ((key1 >> 8) & 0xFF)]++;
}
if (num_vals & 1)
{
const uint key = CRNLIB_GET_KEY(p);
hist[ key & 0xFF]++;
hist[256 + ((key >> 8) & 0xFF)]++;
}
}
else
{
CRNLIB_ASSERT(key_size == 1);
if (key_size != 1)
return NULL;
T* p = pBuf0;
T* q = pBuf0 + (num_vals >> 1) * 2;
for ( ; p != q; p += 2)
{
const uint key0 = CRNLIB_GET_KEY(p);
const uint key1 = CRNLIB_GET_KEY(p+1);
hist[key0 & 0xFF]++;
hist[key1 & 0xFF]++;
}
if (num_vals & 1)
{
const uint key = CRNLIB_GET_KEY(p);
hist[key & 0xFF]++;
}
}
T* pCur = pBuf0;
T* pNew = pBuf1;
for (uint pass = 0; pass < key_size; pass++)
{
const uint* pHist = &hist[pass << 8];
uint offsets[256];
uint cur_ofs = 0;
for (uint i = 0; i < 256; i += 2)
{
offsets[i] = cur_ofs;
cur_ofs += pHist[i];
offsets[i+1] = cur_ofs;
cur_ofs += pHist[i+1];
}
const uint pass_shift = pass << 3;
T* p = pCur;
T* q = pCur + (num_vals >> 1) * 2;
for ( ; p != q; p += 2)
{
uint c0 = (CRNLIB_GET_KEY(p) >> pass_shift) & 0xFF;
uint c1 = (CRNLIB_GET_KEY(p+1) >> pass_shift) & 0xFF;
if (c0 == c1)
{
uint dst_offset0 = offsets[c0];
offsets[c0] = dst_offset0 + 2;
pNew[dst_offset0] = p[0];
pNew[dst_offset0 + 1] = p[1];
}
else
{
uint dst_offset0 = offsets[c0]++;
uint dst_offset1 = offsets[c1]++;
pNew[dst_offset0] = p[0];
pNew[dst_offset1] = p[1];
}
}
if (num_vals & 1)
{
uint c = (CRNLIB_GET_KEY(p) >> pass_shift) & 0xFF;
uint dst_offset = offsets[c];
offsets[c] = dst_offset + 1;
pNew[dst_offset] = *p;
}
T* t = pCur;
pCur = pNew;
pNew = t;
}
return pCur;
}
#undef CRNLIB_GET_KEY
// Returns pointer to sorted array.
template<typename T, typename Q>
T* indirect_radix_sort(uint num_indices, T* pIndices0, T* pIndices1, const Q* pKeys, uint key_ofs, uint key_size, bool init_indices)
{
CRNLIB_ASSERT_OPEN_RANGE(key_ofs, 0, sizeof(T));
CRNLIB_ASSERT_CLOSED_RANGE(key_size, 1, 4);
if (init_indices)
{
T* p = pIndices0;
T* q = pIndices0 + (num_indices >> 1) * 2;
uint i;
for (i = 0; p != q; p += 2, i += 2)
{
p[0] = static_cast<T>(i);
p[1] = static_cast<T>(i + 1);
}
if (num_indices & 1)
*p = static_cast<T>(i);
}
uint hist[256 * 4];
memset(hist, 0, sizeof(hist[0]) * 256 * key_size);
#define CRNLIB_GET_KEY(p) (*(const uint*)((const uint8*)(pKeys + *(p)) + key_ofs))
#define CRNLIB_GET_KEY_FROM_INDEX(i) (*(const uint*)((const uint8*)(pKeys + (i)) + key_ofs))
if (key_size == 4)
{
T* p = pIndices0;
T* q = pIndices0 + num_indices;
for ( ; p != q; p++)
{
const uint key = CRNLIB_GET_KEY(p);
hist[ key & 0xFF]++;
hist[256 + ((key >> 8) & 0xFF)]++;
hist[512 + ((key >> 16) & 0xFF)]++;
hist[768 + ((key >> 24) & 0xFF)]++;
}
}
else if (key_size == 3)
{
T* p = pIndices0;
T* q = pIndices0 + num_indices;
for ( ; p != q; p++)
{
const uint key = CRNLIB_GET_KEY(p);
hist[ key & 0xFF]++;
hist[256 + ((key >> 8) & 0xFF)]++;
hist[512 + ((key >> 16) & 0xFF)]++;
}
}
else if (key_size == 2)
{
T* p = pIndices0;
T* q = pIndices0 + (num_indices >> 1) * 2;
for ( ; p != q; p += 2)
{
const uint key0 = CRNLIB_GET_KEY(p);
const uint key1 = CRNLIB_GET_KEY(p+1);
hist[ key0 & 0xFF]++;
hist[256 + ((key0 >> 8) & 0xFF)]++;
hist[ key1 & 0xFF]++;
hist[256 + ((key1 >> 8) & 0xFF)]++;
}
if (num_indices & 1)
{
const uint key = CRNLIB_GET_KEY(p);
hist[ key & 0xFF]++;
hist[256 + ((key >> 8) & 0xFF)]++;
}
}
else
{
CRNLIB_ASSERT(key_size == 1);
if (key_size != 1)
return NULL;
T* p = pIndices0;
T* q = pIndices0 + (num_indices >> 1) * 2;
for ( ; p != q; p += 2)
{
const uint key0 = CRNLIB_GET_KEY(p);
const uint key1 = CRNLIB_GET_KEY(p+1);
hist[key0 & 0xFF]++;
hist[key1 & 0xFF]++;
}
if (num_indices & 1)
{
const uint key = CRNLIB_GET_KEY(p);
hist[key & 0xFF]++;
}
}
T* pCur = pIndices0;
T* pNew = pIndices1;
for (uint pass = 0; pass < key_size; pass++)
{
const uint* pHist = &hist[pass << 8];
uint offsets[256];
uint cur_ofs = 0;
for (uint i = 0; i < 256; i += 2)
{
offsets[i] = cur_ofs;
cur_ofs += pHist[i];
offsets[i+1] = cur_ofs;
cur_ofs += pHist[i+1];
}
const uint pass_shift = pass << 3;
T* p = pCur;
T* q = pCur + (num_indices >> 1) * 2;
for ( ; p != q; p += 2)
{
uint index0 = p[0];
uint index1 = p[1];
uint c0 = (CRNLIB_GET_KEY_FROM_INDEX(index0) >> pass_shift) & 0xFF;
uint c1 = (CRNLIB_GET_KEY_FROM_INDEX(index1) >> pass_shift) & 0xFF;
if (c0 == c1)
{
uint dst_offset0 = offsets[c0];
offsets[c0] = dst_offset0 + 2;
pNew[dst_offset0] = static_cast<T>(index0);
pNew[dst_offset0 + 1] = static_cast<T>(index1);
}
else
{
uint dst_offset0 = offsets[c0]++;
uint dst_offset1 = offsets[c1]++;
pNew[dst_offset0] = static_cast<T>(index0);
pNew[dst_offset1] = static_cast<T>(index1);
}
}
if (num_indices & 1)
{
uint index = *p;
uint c = (CRNLIB_GET_KEY_FROM_INDEX(index) >> pass_shift) & 0xFF;
uint dst_offset = offsets[c];
offsets[c] = dst_offset + 1;
pNew[dst_offset] = static_cast<T>(index);
}
T* t = pCur;
pCur = pNew;
pNew = t;
}
return pCur;
}
#undef CRNLIB_GET_KEY
#undef CRNLIB_GET_KEY_FROM_INDEX
} // namespace crnlib
+7 -1
View File
@@ -211,7 +211,7 @@ namespace crnlib
return math::clamp<float>(r, l, h); return math::clamp<float>(r, l, h);
} }
int random::irand(int l, int h) int random::irand(int l, int h)
{ {
CRNLIB_ASSERT(l < h); CRNLIB_ASSERT(l < h);
@@ -236,6 +236,12 @@ namespace crnlib
return result; return result;
} }
int random::irand_inclusive(int l, int h)
{
CRNLIB_ASSERT(h < cINT32_MAX);
return irand(l, h + 1);
}
/* /*
ALGORITHM 712, COLLECTED ALGORITHMS FROM ACM. ALGORITHM 712, COLLECTED ALGORITHMS FROM ACM.
THIS WORK PUBLISHED IN TRANSACTIONS ON MATHEMATICAL SOFTWARE, THIS WORK PUBLISHED IN TRANSACTIONS ON MATHEMATICAL SOFTWARE,
+3
View File
@@ -77,6 +77,9 @@ namespace crnlib
// Returns random between [l, h) // Returns random between [l, h)
int irand(int l, int h); int irand(int l, int h);
// Returns random between [l, h]
int irand_inclusive(int l, int h);
double gaussian(double mean, double stddev); double gaussian(double mean, double stddev);
+2 -1
View File
@@ -17,7 +17,8 @@ namespace crnlib
{ {
clear(); clear();
} }
// up to, but not including right/bottom
inline rect(int left, int top, int right, int bottom) inline rect(int left, int top, int right, int bottom)
{ {
set(left, top, right, bottom); set(left, top, right, bottom);
+1 -1
View File
@@ -132,7 +132,7 @@ namespace crnlib
if (xscale < 1.0f) if (xscale < 1.0f)
{ {
int total; int total; (void)total;
/* Handle case when there are fewer destination /* Handle case when there are fewer destination
* samples than source samples (downsampling/minification). * samples than source samples (downsampling/minification).
File diff suppressed because it is too large Load Diff
+80
View File
@@ -0,0 +1,80 @@
// File: rg_etc1.h - Fast, high quality ETC1 block packer/unpacker - Rich Geldreich <richgel99@gmail.com>
// Please see ZLIB license at the end of this file.
#pragma once
namespace crnlib {
namespace rg_etc1
{
// Unpacks an 8-byte ETC1 compressed block to a block of 4x4 32bpp RGBA pixels.
// Returns false if the block is invalid. Invalid blocks will still be unpacked with clamping.
// This function is thread safe, and does not dynamically allocate any memory.
// If preserve_alpha is true, the alpha channel of the destination pixels will not be overwritten. Otherwise, alpha will be set to 255.
bool unpack_etc1_block(const void *pETC1_block, unsigned int* pDst_pixels_rgba, bool preserve_alpha = false);
// Quality setting = the higher the quality, the slower.
// To pack large textures, it is highly recommended to call pack_etc1_block() in parallel, on different blocks, from multiple threads (particularly when using cHighQuality).
enum etc1_quality
{
cLowQuality,
cMediumQuality,
cHighQuality,
};
struct etc1_pack_params
{
etc1_quality m_quality;
bool m_dithering;
inline etc1_pack_params()
{
clear();
}
void clear()
{
m_quality = cHighQuality;
m_dithering = false;
}
};
// Important: pack_etc1_block_init() must be called before calling pack_etc1_block().
void pack_etc1_block_init();
// Packs a 4x4 block of 32bpp RGBA pixels to an 8-byte ETC1 block.
// 32-bit RGBA pixels must always be arranged as (R,G,B,A) (R first, A last) in memory, independent of platform endianness. A should always be 255.
// Returns squared error of result.
// This function is thread safe, and does not dynamically allocate any memory.
// pack_etc1_block() does not currently support "perceptual" colorspace metrics - it primarily optimizes for RGB RMSE.
unsigned int pack_etc1_block(void* pETC1_block, const unsigned int* pSrc_pixels_rgba, etc1_pack_params& pack_params);
} // namespace rg_etc1
} // namespace crnlib
//------------------------------------------------------------------------------
//
// rg_etc1 uses the ZLIB license:
// http://opensource.org/licenses/Zlib
//
// Copyright (c) 2012 Rich Geldreich
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
//------------------------------------------------------------------------------
+1 -1
View File
@@ -2900,7 +2900,7 @@ static int shiftsigned(int v, int shift, int bits)
static stbi_uc *bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp) static stbi_uc *bmp_load(stbi *s, int *x, int *y, int *comp, int req_comp)
{ {
uint8 *out; uint8 *out;
unsigned int mr=0,mg=0,mb=0,ma=0, fake_a=0; unsigned int mr=0,mg=0,mb=0,ma=0, fake_a=0; (void)fake_a;
stbi_uc pal[256][4]; stbi_uc pal[256][4];
int psize=0,i,j,compress=0,width; int psize=0,i,j,compress=0,width;
int bpp, flip_vertically, pad, target, offset, hsz; int bpp, flip_vertically, pad, target, offset, hsz;
+5 -5
View File
@@ -231,7 +231,7 @@ namespace crnlib
return true; return true;
} }
static bool create_dds_tex(const crn_comp_params &params, dds_texture &dds_tex) static bool create_dds_tex(const crn_comp_params &params, mipmapped_texture &dds_tex)
{ {
image_u8 images[cCRNMaxFaces][cCRNMaxLevels]; image_u8 images[cCRNMaxFaces][cCRNMaxLevels];
@@ -281,7 +281,7 @@ namespace crnlib
return true; return true;
} }
bool create_texture_mipmaps(dds_texture &work_tex, const crn_comp_params &params, const crn_mipmap_params &mipmap_params, bool generate_mipmaps) bool create_texture_mipmaps(mipmapped_texture &work_tex, const crn_comp_params &params, const crn_mipmap_params &mipmap_params, bool generate_mipmaps)
{ {
crn_comp_params new_params(params); crn_comp_params new_params(params);
@@ -438,7 +438,7 @@ namespace crnlib
bool srgb = mipmap_params.m_gamma_filtering != 0; bool srgb = mipmap_params.m_gamma_filtering != 0;
dds_texture::resample_params res_params; mipmapped_texture::resample_params res_params;
res_params.m_pFilter = pFilter; res_params.m_pFilter = pFilter;
res_params.m_wrapping = mipmap_params.m_tiled != 0; res_params.m_wrapping = mipmap_params.m_tiled != 0;
if (work_tex.get_num_faces()) if (work_tex.get_num_faces())
@@ -462,7 +462,7 @@ namespace crnlib
const char* pFilter = crn_get_mip_filter_name(mipmap_params.m_filter); const char* pFilter = crn_get_mip_filter_name(mipmap_params.m_filter);
dds_texture::generate_mipmap_params gen_params; mipmapped_texture::generate_mipmap_params gen_params;
gen_params.m_pFilter = pFilter; gen_params.m_pFilter = pFilter;
gen_params.m_wrapping = mipmap_params.m_tiled != 0; gen_params.m_wrapping = mipmap_params.m_tiled != 0;
gen_params.m_renormalize = mipmap_params.m_renormalize != 0; gen_params.m_renormalize = mipmap_params.m_renormalize != 0;
@@ -496,7 +496,7 @@ namespace crnlib
if (pActual_bitrate) *pActual_bitrate = 0.0f; if (pActual_bitrate) *pActual_bitrate = 0.0f;
if (pActual_quality_level) *pActual_quality_level = 0; if (pActual_quality_level) *pActual_quality_level = 0;
dds_texture work_tex; mipmapped_texture work_tex;
if (!create_dds_tex(params, work_tex)) if (!create_dds_tex(params, work_tex))
{ {
console::error("Failed creating DDS texture from crn_comp_params!"); console::error("Failed creating DDS texture from crn_comp_params!");
+2 -2
View File
@@ -6,7 +6,7 @@
namespace crnlib namespace crnlib
{ {
class dds_texture; class mipmapped_texture;
class itexture_comp class itexture_comp
{ {
@@ -27,7 +27,7 @@ namespace crnlib
}; };
bool create_compressed_texture(const crn_comp_params &params, crnlib::vector<uint8> &comp_data, uint32 *pActual_quality_level, float *pActual_bitrate); bool create_compressed_texture(const crn_comp_params &params, crnlib::vector<uint8> &comp_data, uint32 *pActual_quality_level, float *pActual_bitrate);
bool create_texture_mipmaps(dds_texture &work_tex, const crn_comp_params &params, const crn_mipmap_params &mipmap_params, bool generate_mipmaps); bool create_texture_mipmaps(mipmapped_texture &work_tex, const crn_comp_params &params, const crn_mipmap_params &mipmap_params, bool generate_mipmaps);
bool create_compressed_texture(const crn_comp_params &params, const crn_mipmap_params &mipmap_params, crnlib::vector<uint8> &comp_data, uint32 *pActual_quality_level, float *pActual_bitrate); bool create_compressed_texture(const crn_comp_params &params, const crn_mipmap_params &mipmap_params, crnlib::vector<uint8> &comp_data, uint32 *pActual_quality_level, float *pActual_bitrate);
} // namespace crnlib } // namespace crnlib
+125 -70
View File
@@ -28,7 +28,7 @@ namespace crnlib
bool convert_stats::init( bool convert_stats::init(
const char* pSrc_filename, const char* pSrc_filename,
const char* pDst_filename, const char* pDst_filename,
dds_texture& src_tex, mipmapped_texture& src_tex,
texture_file_types::format dst_file_type, texture_file_types::format dst_file_type,
bool lzma_stats) bool lzma_stats)
{ {
@@ -74,7 +74,7 @@ namespace crnlib
} }
} }
if (!m_output_tex.load_from_file(pDst_filename, m_dst_file_type)) if (!m_output_tex.read_from_file(pDst_filename, m_dst_file_type))
{ {
console::error("Failed loading output file: %s", pDst_filename); console::error("Failed loading output file: %s", pDst_filename);
return false; return false;
@@ -130,42 +130,46 @@ namespace crnlib
} }
else else
{ {
uint num_faces = math::minimum(m_pInput_tex->get_num_faces(), m_output_tex.get_num_faces());
uint num_levels = math::minimum(m_pInput_tex->get_num_levels(), m_output_tex.get_num_levels()); uint num_levels = math::minimum(m_pInput_tex->get_num_levels(), m_output_tex.get_num_levels());
if (!mip_stats) if (!mip_stats)
num_levels = 1; num_levels = 1;
for (uint level = 0; level < num_levels; level++) for (uint face = 0; face < num_faces; face++)
{ {
image_u8 a, b; for (uint level = 0; level < num_levels; level++)
image_u8* pA = m_pInput_tex->get_level_image(0, level, a);
image_u8* pB = m_output_tex.get_level_image(0, level, b);
if (pA && pB)
{ {
image_u8 grayscale_a, grayscale_b; image_u8 a, b;
if (grayscale_sampling) image_u8* pA = m_pInput_tex->get_level_image(face, level, a);
image_u8* pB = m_output_tex.get_level_image(face, level, b);
if (pA && pB)
{ {
grayscale_a = *pA; image_u8 grayscale_a, grayscale_b;
grayscale_a.convert_to_grayscale(); if (grayscale_sampling)
pA = &grayscale_a; {
grayscale_a = *pA;
grayscale_a.convert_to_grayscale();
pA = &grayscale_a;
grayscale_b = *pB; grayscale_b = *pB;
grayscale_b.convert_to_grayscale(); grayscale_b.convert_to_grayscale();
pB = &grayscale_b; pB = &grayscale_b;
}
console::info("Face %u Mipmap level %u statistics:", face, level);
image_utils::print_image_metrics(*pA, *pB);
if ((pA->has_rgb()) || (pB->has_rgb()))
image_utils::print_ssim(*pA, *pB);
} }
console::info("Mipmap level %u statistics:", level);
image_utils::print_image_metrics(*pA, *pB);
if ((pA->has_rgb()) || (pB->has_rgb()))
image_utils::print_ssim(*pA, *pB);
} }
} }
if (pCSVStatsFile) if (pCSVStatsFile)
{ {
// FIXME: This is kind of a hack, and should be combine with the code above. // FIXME: This is kind of a hack, and should be combined with the code above.
image_u8 a, b; image_u8 a, b;
image_u8* pA = m_pInput_tex->get_level_image(0, 0, a); image_u8* pA = m_pInput_tex->get_level_image(0, 0, a);
image_u8* pB = m_output_tex.get_level_image(0, 0, b); image_u8* pB = m_output_tex.get_level_image(0, 0, b);
@@ -292,56 +296,63 @@ namespace crnlib
return false; return false;
} }
static pixel_format choose_pixel_format(convert_params& params, const crn_comp_params &comp_params, const dds_texture& src_tex, texture_type tex_type) static pixel_format choose_pixel_format(convert_params& params, const crn_comp_params &comp_params, const mipmapped_texture& src_tex, texture_type tex_type)
{ {
if (params.m_use_source_format) const pixel_format src_fmt = src_tex.get_format();
return src_tex.get_format(); const texture_file_types::format src_file_type = src_tex.get_source_file_type();
const bool is_normal_map = (tex_type == cTextureTypeNormalMap); const bool is_normal_map = (tex_type == cTextureTypeNormalMap);
if (params.m_always_use_source_pixel_format)
return src_fmt;
// Attempt to choose a reasonable/sane output pixel format.
if (params.m_dst_file_type == texture_file_types::cFormatCRN) if (params.m_dst_file_type == texture_file_types::cFormatCRN)
{ {
if (is_normal_map) if (is_normal_map)
{ {
switch (src_tex.get_format()) if (pixel_format_helpers::is_dxt(src_fmt))
return src_fmt;
else
return PIXEL_FMT_DXT5_AGBR;
}
}
else if (params.m_dst_file_type == texture_file_types::cFormatKTX)
{
if ((src_file_type != texture_file_types::cFormatCRN) && (src_file_type != texture_file_types::cFormatKTX) && (src_file_type != texture_file_types::cFormatDDS))
{
if (is_normal_map)
{ {
case PIXEL_FMT_DXN: return pixel_format_helpers::has_alpha(src_fmt) ? PIXEL_FMT_A8R8G8B8 : PIXEL_FMT_R8G8B8;
case PIXEL_FMT_3DC:
case PIXEL_FMT_DXT5_xGBR:
case PIXEL_FMT_DXT5_AGBR:
case PIXEL_FMT_DXT5_xGxR:
return src_tex.get_format();
default:
return PIXEL_FMT_DXT5_AGBR;
} }
else if (pixel_format_helpers::is_grayscale(src_fmt))
{
if (pixel_format_helpers::has_alpha(src_fmt))
return PIXEL_FMT_A8L8;
else
return PIXEL_FMT_ETC1;
}
else if (pixel_format_helpers::has_alpha(src_fmt))
return PIXEL_FMT_A8R8G8B8;
else
return PIXEL_FMT_ETC1;
} }
} }
else if (params.m_dst_file_type == texture_file_types::cFormatDDS) else if (params.m_dst_file_type == texture_file_types::cFormatDDS)
{ {
if (src_tex.get_source_file_type() != texture_file_types::cFormatCRN) if ((src_file_type != texture_file_types::cFormatCRN) && (src_file_type != texture_file_types::cFormatKTX) && (src_file_type != texture_file_types::cFormatDDS))
{ {
if (is_normal_map) if (is_normal_map)
{ {
switch (src_tex.get_format()) return PIXEL_FMT_DXT5_AGBR;
{
case PIXEL_FMT_DXN:
case PIXEL_FMT_3DC:
case PIXEL_FMT_DXT5_xGBR:
case PIXEL_FMT_DXT5_AGBR:
case PIXEL_FMT_DXT5_xGxR:
return src_tex.get_format();
default:
return PIXEL_FMT_DXT5_AGBR;
}
} }
else if (pixel_format_helpers::is_grayscale(src_tex.get_format())) else if (pixel_format_helpers::is_grayscale(src_fmt))
{ {
if (pixel_format_helpers::has_alpha(src_tex.get_format())) if (pixel_format_helpers::has_alpha(src_fmt))
return comp_params.get_flag(cCRNCompFlagDXT1AForTransparency) ? PIXEL_FMT_DXT1A : PIXEL_FMT_DXT5; return comp_params.get_flag(cCRNCompFlagDXT1AForTransparency) ? PIXEL_FMT_DXT1A : PIXEL_FMT_DXT5;
else else
return PIXEL_FMT_DXT1; return PIXEL_FMT_DXT1;
} }
else if (pixel_format_helpers::has_alpha(src_tex.get_format())) else if (pixel_format_helpers::has_alpha(src_fmt))
return comp_params.get_flag(cCRNCompFlagDXT1AForTransparency) ? PIXEL_FMT_DXT1A : PIXEL_FMT_DXT5; return comp_params.get_flag(cCRNCompFlagDXT1AForTransparency) ? PIXEL_FMT_DXT1A : PIXEL_FMT_DXT5;
else else
return PIXEL_FMT_DXT1; return PIXEL_FMT_DXT1;
@@ -349,21 +360,21 @@ namespace crnlib
} }
else else
{ {
// A regular image format. // Destination is a regular image format.
if (pixel_format_helpers::is_grayscale(src_tex.get_format())) if (pixel_format_helpers::is_grayscale(src_fmt))
{ {
if (pixel_format_helpers::has_alpha(src_tex.get_format())) if (pixel_format_helpers::has_alpha(src_fmt))
return PIXEL_FMT_A8L8; return PIXEL_FMT_A8L8;
else else
return PIXEL_FMT_L8; return PIXEL_FMT_L8;
} }
else if (pixel_format_helpers::has_alpha(src_tex.get_format())) else if (pixel_format_helpers::has_alpha(src_fmt))
return PIXEL_FMT_A8R8G8B8; return PIXEL_FMT_A8R8G8B8;
else else
return PIXEL_FMT_R8G8B8; return PIXEL_FMT_R8G8B8;
} }
return src_tex.get_format(); return src_fmt;
} }
static void print_comp_params(const crn_comp_params &comp_params) static void print_comp_params(const crn_comp_params &comp_params)
@@ -404,23 +415,27 @@ namespace crnlib
void convert_params::print() void convert_params::print()
{ {
console::debug("\nTexture conversion parameters:"); console::debug("\nTexture conversion parameters:");
console::debug(" Resolution: %ux%u, Faces: %u, Levels: %u, Format: %s", console::debug(" Resolution: %ux%u, Faces: %u, Levels: %u, Format: %s, X Flipped: %u, Y Flipped: %u",
m_pInput_texture->get_width(), m_pInput_texture->get_width(),
m_pInput_texture->get_height(), m_pInput_texture->get_height(),
m_pInput_texture->get_num_faces(), m_pInput_texture->get_num_faces(),
m_pInput_texture->get_num_levels(), m_pInput_texture->get_num_levels(),
pixel_format_helpers::get_pixel_format_string(m_pInput_texture->get_format())); pixel_format_helpers::get_pixel_format_string(m_pInput_texture->get_format()),
m_pInput_texture->is_x_flipped(),
m_pInput_texture->is_y_flipped());
console::debug(" texture_type: %s", get_texture_type_desc(m_texture_type)); console::debug(" texture_type: %s", get_texture_type_desc(m_texture_type));
console::debug(" dst_filename: %s", m_dst_filename.get_ptr()); console::debug(" dst_filename: %s", m_dst_filename.get_ptr());
console::debug(" dst_file_type: %s", texture_file_types::get_extension(m_dst_file_type)); console::debug(" dst_file_type: %s", texture_file_types::get_extension(m_dst_file_type));
console::debug(" dst_format: %s", pixel_format_helpers::get_pixel_format_string(m_dst_format)); console::debug(" dst_format: %s", pixel_format_helpers::get_pixel_format_string(m_dst_format));
console::debug(" quick: %u", m_quick); console::debug(" quick: %u", m_quick);
console::debug(" use_source_format: %u", m_use_source_format); console::debug(" use_source_format: %u", m_always_use_source_pixel_format);
console::debug(" Y Flip: %u", m_y_flip);
console::debug(" Unflip: %u", m_unflip);
} }
static bool write_compressed_texture( static bool write_compressed_texture(
dds_texture& work_tex, convert_params& params, crn_comp_params &comp_params, pixel_format dst_format, progress_params& progress_state, bool perceptual, convert_stats &stats) mipmapped_texture& work_tex, convert_params& params, crn_comp_params &comp_params, pixel_format dst_format, progress_params& progress_state, bool perceptual, convert_stats &stats)
{ {
comp_params.m_file_type = (params.m_dst_file_type == texture_file_types::cFormatCRN) ? cCRNFileTypeCRN : cCRNFileTypeDDS; comp_params.m_file_type = (params.m_dst_file_type == texture_file_types::cFormatCRN) ? cCRNFileTypeCRN : cCRNFileTypeDDS;
@@ -450,7 +465,7 @@ namespace crnlib
return true; return true;
} }
static bool convert_and_write_normal_texture(dds_texture& work_tex, convert_params& params, const crn_comp_params &comp_params, pixel_format dst_format, progress_params& progress_state, bool formats_differ, bool perceptual, convert_stats& stats) static bool convert_and_write_normal_texture(mipmapped_texture& work_tex, convert_params& params, const crn_comp_params &comp_params, pixel_format dst_format, progress_params& progress_state, bool formats_differ, bool perceptual, convert_stats& stats)
{ {
if (formats_differ) if (formats_differ)
{ {
@@ -517,7 +532,7 @@ namespace crnlib
face_vec face(1); face_vec face(1);
face[0].push_back(crnlib_new<mip_level>(*pLevel)); face[0].push_back(crnlib_new<mip_level>(*pLevel));
dds_texture new_tex; mipmapped_texture new_tex;
new_tex.assign(face); new_tex.assign(face);
console::info("Writing texture face %u mip level %u to file %s", f, l, filename.get_ptr()); console::info("Writing texture face %u mip level %u to file %s", f, l, filename.get_ptr());
@@ -567,9 +582,29 @@ namespace crnlib
params.m_pIntermediate_texture = NULL; params.m_pIntermediate_texture = NULL;
} }
params.m_pIntermediate_texture = crnlib_new<dds_texture>(*params.m_pInput_texture); params.m_pIntermediate_texture = crnlib_new<mipmapped_texture>(*params.m_pInput_texture);
mipmapped_texture& work_tex = *params.m_pInput_texture;
dds_texture& work_tex = *params.m_pInput_texture; if ((params.m_unflip) && (work_tex.is_flipped()))
{
console::info("Unflipping texture");
work_tex.unflip(true, true);
}
if (params.m_y_flip)
{
console::info("Flipping texture on Y axis");
// This is awkward - if we're writing to KTX, then go ahead and properly update the work texture's orientation flags.
// Otherwise, don't bother updating the orientation flags because the writer may then attempt to unflip the texture before writing to formats
// that don't support flipped textures (ugh).
const bool bOutputFormatSupportsFlippedTextures = params.m_dst_file_type == texture_file_types::cFormatKTX;
if (!work_tex.flip_y(bOutputFormatSupportsFlippedTextures))
{
console::warning("Failed flipping texture on Y axis");
}
}
if ((params.m_dst_format != PIXEL_FMT_INVALID) && (pixel_format_helpers::is_alpha_only(params.m_dst_format))) if ((params.m_dst_format != PIXEL_FMT_INVALID) && (pixel_format_helpers::is_alpha_only(params.m_dst_format)))
{ {
@@ -588,13 +623,14 @@ namespace crnlib
if (pixel_format_helpers::is_dxt(dst_format)) if (pixel_format_helpers::is_dxt(dst_format))
{ {
if ((params.m_dst_file_type != texture_file_types::cFormatCRN) && if ((params.m_dst_file_type != texture_file_types::cFormatCRN) &&
(params.m_dst_file_type != texture_file_types::cFormatDDS)) (params.m_dst_file_type != texture_file_types::cFormatDDS) &&
(params.m_dst_file_type != texture_file_types::cFormatKTX))
{ {
console::warning("Output file format does not support DXT - automatically choosing pixel format."); console::warning("Output file format does not support DXTc - automatically choosing a non-DXT pixel format.");
dst_format = PIXEL_FMT_INVALID; dst_format = PIXEL_FMT_INVALID;
} }
} }
if (dst_format == PIXEL_FMT_INVALID) if (dst_format == PIXEL_FMT_INVALID)
{ {
// Caller didn't specify a format to use, so try to pick something reasonable. // Caller didn't specify a format to use, so try to pick something reasonable.
@@ -606,6 +642,18 @@ namespace crnlib
dst_format = PIXEL_FMT_DXT1A; dst_format = PIXEL_FMT_DXT1A;
else if (dst_format == PIXEL_FMT_DXT1A) else if (dst_format == PIXEL_FMT_DXT1A)
comp_params.set_flag(cCRNCompFlagDXT1AForTransparency, true); comp_params.set_flag(cCRNCompFlagDXT1AForTransparency, true);
if ((dst_format == PIXEL_FMT_ETC1) && (params.m_dst_file_type == texture_file_types::cFormatCRN))
{
console::warning("CRN file format does not support ETC1 compressed textures - converting to DXT1 instead.");
dst_format = PIXEL_FMT_DXT1;
}
if ((dst_format == PIXEL_FMT_DXT1A) && (params.m_dst_file_type == texture_file_types::cFormatCRN))
{
console::warning("CRN file format does not support DXT1A compressed textures - converting to DXT5 instead.");
dst_format = PIXEL_FMT_DXT5;
}
const bool is_normal_map = (tex_type == cTextureTypeNormalMap); const bool is_normal_map = (tex_type == cTextureTypeNormalMap);
bool perceptual = comp_params.get_flag(cCRNCompFlagPerceptual); bool perceptual = comp_params.get_flag(cCRNCompFlagPerceptual);
@@ -633,7 +681,9 @@ namespace crnlib
} }
bool generate_mipmaps = texture_file_types::supports_mipmaps(params.m_dst_file_type); bool generate_mipmaps = texture_file_types::supports_mipmaps(params.m_dst_file_type);
if ((params.m_write_mipmaps_to_multiple_files) && ((params.m_dst_file_type != texture_file_types::cFormatCRN) && (params.m_dst_file_type != texture_file_types::cFormatDDS))) if ( (params.m_write_mipmaps_to_multiple_files) &&
((params.m_dst_file_type != texture_file_types::cFormatCRN) && (params.m_dst_file_type != texture_file_types::cFormatDDS) && (params.m_dst_file_type != texture_file_types::cFormatKTX))
)
{ {
generate_mipmaps = true; generate_mipmaps = true;
} }
@@ -657,13 +707,14 @@ namespace crnlib
} }
bool status = false; bool status = false;
timer t; timer t;
t.start(); t.start();
if ( (params.m_dst_file_type == texture_file_types::cFormatCRN) || if ( (params.m_dst_file_type == texture_file_types::cFormatCRN) ||
( (params.m_dst_file_type == texture_file_types::cFormatDDS) && (pixel_format_helpers::is_dxt(dst_format)) && ( (params.m_dst_file_type == texture_file_types::cFormatDDS) && (pixel_format_helpers::is_dxt(dst_format)) &&
((formats_differ) || (comp_params.m_target_bitrate > 0.0f) || (comp_params.m_quality_level < cCRNMaxQualityLevel)) //((formats_differ) || (comp_params.m_target_bitrate > 0.0f) || (comp_params.m_quality_level < cCRNMaxQualityLevel))
((comp_params.m_target_bitrate > 0.0f) || (comp_params.m_quality_level < cCRNMaxQualityLevel))
) )
) )
{ {
@@ -671,6 +722,10 @@ namespace crnlib
} }
else else
{ {
if ((comp_params.m_target_bitrate > 0.0f) || (comp_params.m_quality_level < cCRNMaxQualityLevel))
{
console::warning( "Target bitrate/quality level is not supported for this output file format.\n");
}
status = convert_and_write_normal_texture(work_tex, params, comp_params, dst_format, progress_state, formats_differ, perceptual, stats); status = convert_and_write_normal_texture(work_tex, params, comp_params, dst_format, progress_state, formats_differ, perceptual, stats);
} }
+16 -12
View File
@@ -2,7 +2,7 @@
// See Copyright Notice and license at the end of inc/crnlib.h // See Copyright Notice and license at the end of inc/crnlib.h
#pragma once #pragma once
#include "crn_dxt_image.h" #include "crn_dxt_image.h"
#include "crn_dds_texture.h" #include "crn_mipmapped_texture.h"
#include "crn_rect.h" #include "crn_rect.h"
#include "crn_lzma_codec.h" #include "crn_lzma_codec.h"
@@ -18,7 +18,7 @@ namespace crnlib
bool init( bool init(
const char* pSrc_filename, const char* pSrc_filename,
const char* pDst_filename, const char* pDst_filename,
dds_texture& src_tex, mipmapped_texture& src_tex,
texture_file_types::format dst_file_type, texture_file_types::format dst_file_type,
bool lzma_stats); bool lzma_stats);
@@ -26,12 +26,12 @@ namespace crnlib
void clear(); void clear();
dynamic_string m_src_filename; dynamic_string m_src_filename;
dynamic_string m_dst_filename; dynamic_string m_dst_filename;
texture_file_types::format m_dst_file_type; texture_file_types::format m_dst_file_type;
dds_texture* m_pInput_tex; mipmapped_texture* m_pInput_tex;
dds_texture m_output_tex; mipmapped_texture m_output_tex;
uint64 m_input_file_size; uint64 m_input_file_size;
uint m_total_input_pixels; uint m_total_input_pixels;
@@ -53,12 +53,14 @@ namespace crnlib
m_pProgress_func(NULL), m_pProgress_func(NULL),
m_pProgress_user_data(NULL), m_pProgress_user_data(NULL),
m_pIntermediate_texture(NULL), m_pIntermediate_texture(NULL),
m_y_flip(false),
m_unflip(false),
m_always_use_source_pixel_format(false),
m_write_mipmaps_to_multiple_files(false), m_write_mipmaps_to_multiple_files(false),
m_quick(false), m_quick(false),
m_debugging(false), m_debugging(false),
m_param_debugging(false), m_param_debugging(false),
m_no_stats(false), m_no_stats(false),
m_use_source_format(false),
m_lzma_stats(false), m_lzma_stats(false),
m_status(false), m_status(false),
m_canceled(false) m_canceled(false)
@@ -73,11 +75,11 @@ namespace crnlib
void print(); void print();
// Input parameters // Input parameters
dds_texture* m_pInput_texture; mipmapped_texture* m_pInput_texture;
texture_type m_texture_type; texture_type m_texture_type;
dynamic_string m_dst_filename; dynamic_string m_dst_filename;
texture_file_types::format m_dst_file_type; texture_file_types::format m_dst_file_type;
pixel_format m_dst_format; pixel_format m_dst_format;
@@ -89,15 +91,17 @@ namespace crnlib
void* m_pProgress_user_data; void* m_pProgress_user_data;
// Return parameters // Return parameters
dds_texture* m_pIntermediate_texture; mipmapped_texture* m_pIntermediate_texture;
mutable dynamic_string m_error_message; mutable dynamic_string m_error_message;
bool m_y_flip;
bool m_unflip;
bool m_always_use_source_pixel_format;
bool m_write_mipmaps_to_multiple_files; bool m_write_mipmaps_to_multiple_files;
bool m_quick; bool m_quick;
bool m_debugging; bool m_debugging;
bool m_param_debugging; bool m_param_debugging;
bool m_no_stats; bool m_no_stats;
bool m_use_source_format;
bool m_lzma_stats; bool m_lzma_stats;
mutable bool m_status; mutable bool m_status;
+6 -2
View File
@@ -14,6 +14,10 @@ namespace crnlib
static const char* extensions[cNumFileFormats] = static const char* extensions[cNumFileFormats] =
{ {
"dds",
"crn",
"ktx",
"tga", "tga",
"png", "png",
"jpg", "jpg",
@@ -24,10 +28,9 @@ namespace crnlib
"tiff", "tiff",
"ppm", "ppm",
"pgm", "pgm",
"dds",
"psd", "psd",
"jp2", "jp2",
"crn",
"<clipboard>", "<clipboard>",
"<dragdrop>" "<dragdrop>"
}; };
@@ -59,6 +62,7 @@ namespace crnlib
{ {
case cFormatCRN: case cFormatCRN:
case cFormatDDS: case cFormatDDS:
case cFormatKTX:
return true; return true;
default: break; default: break;
} }
+10 -4
View File
@@ -13,7 +13,13 @@ namespace crnlib
{ {
cFormatInvalid = -1, cFormatInvalid = -1,
cFormatTGA = 0, cFormatDDS,
cFormatCRN,
cFormatKTX,
cNumMipmappedFileFormats,
cFormatTGA = cNumMipmappedFileFormats,
cFormatPNG, cFormatPNG,
cFormatJPG, cFormatJPG,
cFormatJPEG, cFormatJPEG,
@@ -23,12 +29,12 @@ namespace crnlib
cFormatTIFF, cFormatTIFF,
cFormatPPM, cFormatPPM,
cFormatPGM, cFormatPGM,
cFormatDDS,
cFormatPSD, cFormatPSD,
cFormatJP2, cFormatJP2,
cFormatCRN,
cNumRegularFileFormats, cNumRegularFileFormats,
cNumImageFileFormats = cNumRegularFileFormats - cNumMipmappedFileFormats,
// Not really a file format // Not really a file format
cFormatClipboard = cNumRegularFileFormats, cFormatClipboard = cNumRegularFileFormats,
-2
View File
@@ -305,7 +305,6 @@ namespace crnlib
bool task_pool::queue_task(task_callback_func pFunc, uint64 data, void* pData_ptr) bool task_pool::queue_task(task_callback_func pFunc, uint64 data, void* pData_ptr)
{ {
CRNLIB_ASSERT(m_num_threads);
CRNLIB_ASSERT(pFunc); CRNLIB_ASSERT(pFunc);
task tsk; task tsk;
@@ -329,7 +328,6 @@ namespace crnlib
// It's the object's responsibility to delete pObj within the execute_task() method, if needed! // It's the object's responsibility to delete pObj within the execute_task() method, if needed!
bool task_pool::queue_task(executable_task* pObj, uint64 data, void* pData_ptr) bool task_pool::queue_task(executable_task* pObj, uint64 data, void* pData_ptr)
{ {
CRNLIB_ASSERT(m_num_threads);
CRNLIB_ASSERT(pObj); CRNLIB_ASSERT(pObj);
task tsk; task tsk;
-1
View File
@@ -299,7 +299,6 @@ namespace crnlib
template<typename S, typename T> template<typename S, typename T>
inline bool task_pool::queue_multiple_object_tasks(S* pObject, T pObject_method, uint64 first_data, uint num_tasks, void* pData_ptr) inline bool task_pool::queue_multiple_object_tasks(S* pObject, T pObject_method, uint64 first_data, uint num_tasks, void* pData_ptr)
{ {
CRNLIB_ASSERT(m_num_threads);
CRNLIB_ASSERT(pObject); CRNLIB_ASSERT(pObject);
CRNLIB_ASSERT(num_tasks); CRNLIB_ASSERT(num_tasks);
if (!num_tasks) if (!num_tasks)
+2 -3
View File
@@ -325,7 +325,6 @@ namespace crnlib
bool task_pool::queue_task(task_callback_func pFunc, uint64 data, void* pData_ptr) bool task_pool::queue_task(task_callback_func pFunc, uint64 data, void* pData_ptr)
{ {
CRNLIB_ASSERT(m_num_threads);
CRNLIB_ASSERT(pFunc); CRNLIB_ASSERT(pFunc);
task tsk; task tsk;
@@ -350,7 +349,6 @@ namespace crnlib
// It's the object's responsibility to delete pObj within the execute_task() method, if needed! // It's the object's responsibility to delete pObj within the execute_task() method, if needed!
bool task_pool::queue_task(executable_task* pObj, uint64 data, void* pData_ptr) bool task_pool::queue_task(executable_task* pObj, uint64 data, void* pData_ptr)
{ {
CRNLIB_ASSERT(m_num_threads);
CRNLIB_ASSERT(pObj); CRNLIB_ASSERT(pObj);
task tsk; task tsk;
@@ -426,4 +424,5 @@ namespace crnlib
return 0; return 0;
} }
} } // namespace crnlib
-1
View File
@@ -370,7 +370,6 @@ namespace crnlib
template<typename S, typename T> template<typename S, typename T>
inline bool task_pool::queue_multiple_object_tasks(S* pObject, T pObject_method, uint64 first_data, uint num_tasks, void* pData_ptr) inline bool task_pool::queue_multiple_object_tasks(S* pObject, T pObject_method, uint64 first_data, uint num_tasks, void* pData_ptr)
{ {
CRNLIB_ASSERT(m_num_threads);
CRNLIB_ASSERT(pObject); CRNLIB_ASSERT(pObject);
CRNLIB_ASSERT(num_tasks); CRNLIB_ASSERT(num_tasks);
if (!num_tasks) if (!num_tasks)
+18
View File
@@ -42,4 +42,22 @@ namespace crnlib
bool m_stopped : 1; bool m_stopped : 1;
}; };
// Prints object's lifetime to stdout
class timed_scope
{
const char* m_pName;
timer m_tm;
public:
inline timed_scope(char* pName = "timed_scope") : m_pName(pName) { m_tm.start(); }
inline double get_elapsed_secs() const { return m_tm.get_elapsed_secs(); }
inline double get_elapsed_ms() const { return m_tm.get_elapsed_ms(); }
const timer &get_timer() const { return m_tm; }
timer &get_timer() { return m_tm; }
inline ~timed_scope() { double secs = m_tm.get_elapsed_secs(); printf("%s: %f secs, %f ms\n", m_pName, secs, secs * 1000.0f); }
};
} // namespace crnlib } // namespace crnlib
+6 -2
View File
@@ -71,12 +71,16 @@ namespace crnlib
struct bitwise_movable { enum { cFlag = false }; }; struct bitwise_movable { enum { cFlag = false }; };
// Defines type Q as bitwise movable. // Defines type Q as bitwise movable.
// Bitwise movable: type T may be safely moved to a new location via memcpy, without requiring the old copy to be destructed.
// However, the final version of the object (wherever it winds up in memory) must be eventually destructed (a single time, of course).
// Bitwise movable is a superset of bitwise copyable (all bitwise copyable types are also bitwise movable).
#define CRNLIB_DEFINE_BITWISE_MOVABLE(Q) template<> struct bitwise_movable<Q> { enum { cFlag = true }; }; #define CRNLIB_DEFINE_BITWISE_MOVABLE(Q) template<> struct bitwise_movable<Q> { enum { cFlag = true }; };
template<typename T> template<typename T>
struct bitwise_copyable { enum { cFlag = false }; }; struct bitwise_copyable { enum { cFlag = false }; };
// Defines type Q as bitwise copyable. // Defines type Q as bitwise copyable.
// Bitwise copyable: type T may be safely and freely copied (duplicated) via memcpy, and *does not* require destruction.
#define CRNLIB_DEFINE_BITWISE_COPYABLE(Q) template<> struct bitwise_copyable<Q> { enum { cFlag = true }; }; #define CRNLIB_DEFINE_BITWISE_COPYABLE(Q) template<> struct bitwise_copyable<Q> { enum { cFlag = true }; };
#define CRNLIB_IS_POD(T) __is_pod(T) #define CRNLIB_IS_POD(T) __is_pod(T)
@@ -85,7 +89,7 @@ namespace crnlib
#define CRNLIB_IS_BITWISE_COPYABLE(T) (CRNLIB_IS_SCALAR_TYPE(T) || CRNLIB_IS_POD(T) || (bitwise_copyable<T>::cFlag)) #define CRNLIB_IS_BITWISE_COPYABLE(T) (CRNLIB_IS_SCALAR_TYPE(T) || CRNLIB_IS_POD(T) || (bitwise_copyable<T>::cFlag))
#define CRNLIB_IS_BITWISE_MOVABLE(T) (CRNLIB_IS_BITWISE_COPYABLE(T) || (bitwise_movable<T>::cFlag)) #define CRNLIB_IS_BITWISE_COPYABLE_OR_MOVABLE(T) (CRNLIB_IS_BITWISE_COPYABLE(T) || (bitwise_movable<T>::cFlag))
#define CRNLIB_HAS_DESTRUCTOR(T) ((!scalar_type<T>::cFlag) && (!__is_pod(T))) #define CRNLIB_HAS_DESTRUCTOR(T) ((!scalar_type<T>::cFlag) && (!__is_pod(T)))
+2
View File
@@ -66,6 +66,8 @@ namespace crnlib
m_nodes.push_back(root); m_nodes.push_back(root);
// Warning: if this code is NOT compiled with -fno-strict-aliasing, m_nodes.get_ptr() can be NULL here. (Argh!)
uint total_leaves = 1; uint total_leaves = 1;
while (total_leaves < max_size) while (total_leaves < max_size)
+45 -8
View File
@@ -155,16 +155,53 @@ namespace crnlib
return true; return true;
} }
static inline uint16 swap16(uint16 x) { return static_cast<uint16>((x << 8U) | (x >> 8U)); } #if defined(_MSC_VER)
static inline uint32 swap32(uint32 x) { return ((x << 24U) | ((x << 8U) & 0x00FF0000U) | ((x >> 8U) & 0x0000FF00U) | (x >> 24U)); } static CRNLIB_FORCE_INLINE uint16 swap16(uint16 x) { return _byteswap_ushort(x); }
static CRNLIB_FORCE_INLINE uint32 swap32(uint32 x) { return _byteswap_ulong(x); }
static CRNLIB_FORCE_INLINE uint64 swap64(uint64 x) { return _byteswap_uint64(x); }
#elif defined(__GNUC__)
static CRNLIB_FORCE_INLINE uint16 swap16(uint16 x) { return static_cast<uint16>((x << 8U) | (x >> 8U)); }
static CRNLIB_FORCE_INLINE uint32 swap32(uint32 x) { return __builtin_bswap32(x); }
static CRNLIB_FORCE_INLINE uint64 swap64(uint64 x) { return __builtin_bswap64(x); }
#else
static CRNLIB_FORCE_INLINE uint16 swap16(uint16 x) { return static_cast<uint16>((x << 8U) | (x >> 8U)); }
static CRNLIB_FORCE_INLINE uint32 swap32(uint32 x) { return ((x << 24U) | ((x << 8U) & 0x00FF0000U) | ((x >> 8U) & 0x0000FF00U) | (x >> 24U)); }
static CRNLIB_FORCE_INLINE uint64 swap64(uint64 x) { return (static_cast<uint64>(swap32(static_cast<uint32>(x))) << 32ULL) | swap32(static_cast<uint32>(x >> 32U)); }
#endif
inline uint16 swap_le16_to_native(uint16 x) { return c_crnlib_little_endian_platform ? x : swap16(x); } // Assumes x has been read from memory as a little endian value, converts to native endianness for manipulation.
inline uint32 swap_le32_to_native(uint32 x) { return c_crnlib_little_endian_platform ? x : swap32(x); } CRNLIB_FORCE_INLINE uint16 swap_le16_to_native(uint16 x) { return c_crnlib_little_endian_platform ? x : swap16(x); }
inline uint16 swap_be16_to_native(uint16 x) { return c_crnlib_big_endian_platform ? x : swap16(x); } CRNLIB_FORCE_INLINE uint32 swap_le32_to_native(uint32 x) { return c_crnlib_little_endian_platform ? x : swap32(x); }
inline uint32 swap_be32_to_native(uint32 x) { return c_crnlib_big_endian_platform ? x : swap32(x); } CRNLIB_FORCE_INLINE uint64 swap_le64_to_native(uint64 x) { return c_crnlib_little_endian_platform ? x : swap64(x); }
inline uint32 read_le32(const void* p) { return swap_le32_to_native(*static_cast<const uint32*>(p)); } // Assumes x has been read from memory as a big endian value, converts to native endianness for manipulation.
inline void write_le32(void* p, uint32 x) { *static_cast<uint32*>(p) = swap_le32_to_native(x); } CRNLIB_FORCE_INLINE uint16 swap_be16_to_native(uint16 x) { return c_crnlib_big_endian_platform ? x : swap16(x); }
CRNLIB_FORCE_INLINE uint32 swap_be32_to_native(uint32 x) { return c_crnlib_big_endian_platform ? x : swap32(x); }
CRNLIB_FORCE_INLINE uint64 swap_be64_to_native(uint64 x) { return c_crnlib_big_endian_platform ? x : swap64(x); }
CRNLIB_FORCE_INLINE uint32 read_le32(const void* p) { return swap_le32_to_native(*static_cast<const uint32*>(p)); }
CRNLIB_FORCE_INLINE void write_le32(void* p, uint32 x) { *static_cast<uint32*>(p) = swap_le32_to_native(x); }
CRNLIB_FORCE_INLINE uint64 read_le64(const void* p) { return swap_le64_to_native(*static_cast<const uint64*>(p)); }
CRNLIB_FORCE_INLINE void write_le64(void* p, uint64 x) { *static_cast<uint64*>(p) = swap_le64_to_native(x); }
CRNLIB_FORCE_INLINE uint32 read_be32(const void* p) { return swap_be32_to_native(*static_cast<const uint32*>(p)); }
CRNLIB_FORCE_INLINE void write_be32(void* p, uint32 x) { *static_cast<uint32*>(p) = swap_be32_to_native(x); }
CRNLIB_FORCE_INLINE uint64 read_be64(const void* p) { return swap_be64_to_native(*static_cast<const uint64*>(p)); }
CRNLIB_FORCE_INLINE void write_be64(void* p, uint64 x) { *static_cast<uint64*>(p) = swap_be64_to_native(x); }
inline void endian_swap_mem16(uint16* p, uint n) { while (n--) { *p = swap16(*p); ++p; } }
inline void endian_swap_mem32(uint32* p, uint n) { while (n--) { *p = swap32(*p); ++p; } }
inline void endian_swap_mem64(uint64* p, uint n) { while (n--) { *p = swap64(*p); ++p; } }
inline void endian_swap_mem(void* p, uint size_in_bytes, uint type_size)
{
switch (type_size)
{
case sizeof(uint16): endian_swap_mem16(static_cast<uint16*>(p), size_in_bytes / type_size); break;
case sizeof(uint32): endian_swap_mem32(static_cast<uint32*>(p), size_in_bytes / type_size); break;
case sizeof(uint64): endian_swap_mem64(static_cast<uint64*>(p), size_in_bytes / type_size); break;
}
}
inline void fast_memset(void* pDst, int val, size_t size) inline void fast_memset(void* pDst, int val, size_t size)
{ {
+1 -1
View File
@@ -18,7 +18,7 @@ namespace crnlib
inline void clear() { m_bounds[0].clear(); m_bounds[1].clear(); } inline void clear() { m_bounds[0].clear(); m_bounds[1].clear(); }
inline const T& operator[] (uint i) const { CRNLIB_ASSERT(i < 2); return m_bounds[i]; } inline const T& operator[] (uint i) const { CRNLIB_ASSERT(i < 2); return m_bounds[i]; }
inline T& operator[] (uint i) { CRNLIB_ASSERT(i < 2); return m_bounds[i]; } inline T& operator[] (uint i) { CRNLIB_ASSERT(i < 2); return m_bounds[i]; }
private: private:
T m_bounds[2]; T m_bounds[2];
+64 -12
View File
@@ -138,6 +138,7 @@ namespace crnlib
inline const T* get_ptr() const { return m_p; } inline const T* get_ptr() const { return m_p; }
inline T* get_ptr() { return m_p; } inline T* get_ptr() { return m_p; }
// clear() sets the container to empty, then frees the allocated block.
inline void clear() inline void clear()
{ {
if (m_p) if (m_p)
@@ -149,7 +150,7 @@ namespace crnlib
m_capacity = 0; m_capacity = 0;
} }
} }
inline void clear_no_destruction() inline void clear_no_destruction()
{ {
if (m_p) if (m_p)
@@ -181,6 +182,7 @@ namespace crnlib
return increase_capacity(new_capacity, true, true); return increase_capacity(new_capacity, true, true);
} }
// resize(0) sets the container to empty, but does not free the allocated block.
inline void resize(uint new_size, bool grow_hint = false) inline void resize(uint new_size, bool grow_hint = false)
{ {
if (m_size != new_size) if (m_size != new_size)
@@ -222,6 +224,16 @@ namespace crnlib
return true; return true;
} }
// If size >= capacity/2, reset() sets the container's size to 0 but doesn't free the allocated block (because the container may be similarly loaded in the future).
// Otherwise it blows away the allocated block. See http://www.codercorner.com/blog/?p=494
inline void reset()
{
if (m_size >= (m_capacity >> 1))
resize(0);
else
clear();
}
inline T* enlarge(uint i) inline T* enlarge(uint i)
{ {
uint cur_size = m_size; uint cur_size = m_size;
@@ -364,17 +376,15 @@ namespace crnlib
const T* pSrc = m_p + start + n; const T* pSrc = m_p + start + n;
if ((CRNLIB_IS_BITWISE_COPYABLE(T)) && (!CRNLIB_IS_BITWISE_MOVABLE(T))) if (CRNLIB_IS_BITWISE_COPYABLE_OR_MOVABLE(T))
{ {
// Type is bitwise copyable, so there's no need to destruct the overwritten objects. // This test is overly cautious.
// Copy "down" the objects to preserve, filling in the empty slots. if ((!CRNLIB_IS_BITWISE_COPYABLE(T)) || (CRNLIB_HAS_DESTRUCTOR(T)))
memmove(pDst, pSrc, num_to_move * sizeof(T)); {
} // Type has been marked explictly as bitwise movable, which means we can move them around but they may need to be destructed.
else if (CRNLIB_IS_BITWISE_MOVABLE(T)) // First destroy the erased objects.
{ scalar_type<T>::destruct_array(pDst, n);
// Type is bitwise movable, which means we can move them around but they still may need to be destructed. }
// First destroy the erased objects.
scalar_type<T>::destruct_array(pDst, n);
// Copy "down" the objects to preserve, filling in the empty slots. // Copy "down" the objects to preserve, filling in the empty slots.
memmove(pDst, pSrc, num_to_move * sizeof(T)); memmove(pDst, pSrc, num_to_move * sizeof(T));
@@ -601,6 +611,7 @@ namespace crnlib
} }
} }
// Caller assumes ownership of the heap block associated with the container. Container is cleared.
inline void *assume_ownership() inline void *assume_ownership()
{ {
T* p = m_p; T* p = m_p;
@@ -610,6 +621,45 @@ namespace crnlib
return p; return p;
} }
// Caller is granting ownership of the indicated heap block.
// Block must have size constructed elements, and have enough room for capacity elements.
inline bool grant_ownership(T* p, uint size, uint capacity)
{
// To to prevent the caller from obviously shooting themselves in the foot.
if (((p + capacity) > m_p) && (p < (m_p + m_capacity)))
{
// Can grant ownership of a block inside the container itself!
CRNLIB_ASSERT(0);
return false;
}
if (size > capacity)
{
CRNLIB_ASSERT(0);
return false;
}
if (!p)
{
if (capacity)
{
CRNLIB_ASSERT(0);
return false;
}
}
else if (!capacity)
{
CRNLIB_ASSERT(0);
return false;
}
clear();
m_p = p;
m_size = size;
m_capacity = capacity;
return true;
}
private: private:
T* m_p; T* m_p;
uint m_size; uint m_size;
@@ -638,10 +688,12 @@ namespace crnlib
{ {
return reinterpret_cast<elemental_vector*>(this)->increase_capacity( return reinterpret_cast<elemental_vector*>(this)->increase_capacity(
min_new_capacity, grow_hint, sizeof(T), min_new_capacity, grow_hint, sizeof(T),
(CRNLIB_IS_BITWISE_MOVABLE(T) || (is_vector<T>::cFlag)) ? NULL : object_mover, nofail); (CRNLIB_IS_BITWISE_COPYABLE_OR_MOVABLE(T) || (is_vector<T>::cFlag)) ? NULL : object_mover, nofail);
} }
}; };
typedef crnlib::vector<uint8> uint8_vec;
template<typename T> struct bitwise_movable< vector<T> > { enum { cFlag = true }; }; template<typename T> struct bitwise_movable< vector<T> > { enum { cFlag = true }; };
extern void vector_test(); extern void vector_test();
+155
View File
@@ -0,0 +1,155 @@
// File: crn_vector2d.h
#pragma once
namespace crnlib
{
template <typename T>
class vector2D
{
public:
typedef crnlib::vector<T> vector_type;
typedef T value_type;
typedef T& reference;
typedef const T& const_reference;
typedef T* pointer;
typedef const T* const_pointer;
inline vector2D(uint width = 0, uint height = 0, const T& def = T()) :
m_width(width),
m_height(height),
m_vec(width * height),
m_def(def)
{
}
inline vector2D(const vector2D& other) :
m_width(other.m_width),
m_height(other.m_height),
m_vec(other.m_vec),
m_def(other.m_def)
{
}
inline vector2D& operator= (const vector2D& rhs)
{
if (this == &rhs)
return *this;
m_width = rhs.m_width;
m_height = rhs.m_height;
m_vec = rhs.m_vec;
return *this;
}
bool try_resize(uint width, uint height, bool preserve = true)
{
if ((width == m_width) && (height == m_height))
return true;
vector_type new_vec;
if (!new_vec.try_resize(width * height))
return false;
if (preserve)
{
const uint nx = math::minimum(width, m_width);
const uint ny = math::minimum(height, m_height);
for (uint y = 0; y < ny; y++)
{
const T* pSrc = &m_vec[y * m_width];
T* pDst = &new_vec[y * width];
if (CRNLIB_IS_BITWISE_COPYABLE_OR_MOVABLE(T))
memcpy(pDst, pSrc, nx * sizeof(T));
else
{
for (uint x = 0; x < nx; x++)
*pDst++ = *pSrc++;
}
}
}
m_width = width;
m_height = height;
m_vec.swap(new_vec);
return true;
}
void resize(uint width, uint height, bool preserve = true)
{
if (!try_resize(width, height, preserve))
{
CRNLIB_FAIL("vector2D::resize: Out of memory");
}
}
inline void clear()
{
m_vec.clear();
m_width = 0;
m_height = 0;
}
inline uint width() const { return m_width; }
inline uint height() const { return m_height; }
inline uint size() const { return m_vec.size(); }
inline uint size_in_bytes() const { return m_vec.size() * sizeof(T); }
const vector_type& get_vec() const { return m_vec; }
vector_type& get_vec() { return m_vec; }
inline const T* get_ptr() const { return m_vec.get_ptr(); }
inline T* get_ptr() { return m_vec.get_ptr(); }
inline const T& operator[] (uint i) const { return m_vec[i]; }
inline T& operator[] (uint i) { return m_vec[i]; }
inline const T& operator() (uint x, uint y) const
{
CRNLIB_ASSERT((x < m_width) && (y < m_height));
return m_vec[x + y * m_width];
}
inline T& operator() (uint x, uint y)
{
CRNLIB_ASSERT((x < m_width) && (y < m_height));
return m_vec[x + y * m_width];
}
inline const T& at (uint x, uint y) const
{
if ((x >= m_width) || (y >= m_height))
return m_def;
return m_vec[x + y * m_width];
}
inline T& at (uint x, uint y)
{
if ((x >= m_width) || (y >= m_height))
return m_def;
return m_vec[x + y * m_width];
}
inline void swap(vector2D& other)
{
m_vec.swap(other.m_vec);
anvil::swap(m_width, other.m_width);
anvil::swap(m_height, other.m_height);
}
inline void set_all(const T& x)
{
m_vec.set_all(x);
}
private:
vector_type m_vec;
uint m_width;
uint m_height;
T m_def;
};
} // namespace anvil
+204 -23
View File
@@ -771,6 +771,10 @@
RelativePath=".\crn_vector.h" RelativePath=".\crn_vector.h"
> >
</File> </File>
<File
RelativePath=".\crn_vector2d.h"
>
</File>
</Filter> </Filter>
<Filter <Filter
Name="image" Name="image"
@@ -779,22 +783,6 @@
RelativePath=".\crn_color.h" RelativePath=".\crn_color.h"
> >
</File> </File>
<File
RelativePath=".\crn_dds_texture.cpp"
>
</File>
<File
RelativePath=".\crn_dds_texture.h"
>
</File>
<File
RelativePath=".\crn_dxt_image.cpp"
>
</File>
<File
RelativePath=".\crn_dxt_image.h"
>
</File>
<File <File
RelativePath=".\crn_image.h" RelativePath=".\crn_image.h"
> >
@@ -807,6 +795,150 @@
RelativePath=".\crn_image_utils.h" RelativePath=".\crn_image_utils.h"
> >
</File> </File>
<File
RelativePath=".\crn_jpgd.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug_DLL|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug_DLL|x64"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
/>
</FileConfiguration>
<FileConfiguration
Name="Release_DLL|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
/>
</FileConfiguration>
<FileConfiguration
Name="Release_DLL|x64"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
/>
</FileConfiguration>
</File>
<File
RelativePath=".\crn_jpgd.h"
>
</File>
<File
RelativePath=".\crn_jpge.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug_DLL|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug_DLL|x64"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
/>
</FileConfiguration>
<FileConfiguration
Name="Release_DLL|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
/>
</FileConfiguration>
<FileConfiguration
Name="Release_DLL|x64"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="0"
/>
</FileConfiguration>
</File>
<File
RelativePath=".\crn_jpge.h"
>
</File>
<File <File
RelativePath=".\crn_pixel_format.cpp" RelativePath=".\crn_pixel_format.cpp"
> >
@@ -971,6 +1103,14 @@
RelativePath=".\crn_dxt_hc_common.h" RelativePath=".\crn_dxt_hc_common.h"
> >
</File> </File>
<File
RelativePath=".\crn_etc.cpp"
>
</File>
<File
RelativePath=".\crn_etc.h"
>
</File>
<File <File
RelativePath=".\crn_huffman_codes.cpp" RelativePath=".\crn_huffman_codes.cpp"
> >
@@ -987,6 +1127,14 @@
RelativePath=".\crn_lzma_codec.h" RelativePath=".\crn_lzma_codec.h"
> >
</File> </File>
<File
RelativePath=".\crn_miniz.cpp"
>
</File>
<File
RelativePath=".\crn_miniz.h"
>
</File>
<File <File
RelativePath=".\crn_prefix_coding.cpp" RelativePath=".\crn_prefix_coding.cpp"
> >
@@ -1011,6 +1159,14 @@
RelativePath=".\crn_qdxt5.h" RelativePath=".\crn_qdxt5.h"
> >
</File> </File>
<File
RelativePath=".\crn_rg_etc1.cpp"
>
</File>
<File
RelativePath=".\crn_rg_etc1.h"
>
</File>
<File <File
RelativePath=".\crn_ryg_dxt.cpp" RelativePath=".\crn_ryg_dxt.cpp"
> >
@@ -1111,6 +1267,10 @@
RelativePath=".\crn_packed_uint.h" RelativePath=".\crn_packed_uint.h"
> >
</File> </File>
<File
RelativePath=".\crn_radix_sort.h"
>
</File>
<File <File
RelativePath=".\crn_timer.cpp" RelativePath=".\crn_timer.cpp"
> >
@@ -1194,13 +1354,6 @@
<File <File
RelativePath=".\crn_threading_win32.cpp" RelativePath=".\crn_threading_win32.cpp"
> >
<FileConfiguration
Name="Release|x64"
>
<Tool
Name="VCCLCompilerTool"
/>
</FileConfiguration>
</File> </File>
<File <File
RelativePath=".\crn_threading_win32.h" RelativePath=".\crn_threading_win32.h"
@@ -1447,6 +1600,34 @@
> >
</File> </File>
</Filter> </Filter>
<Filter
Name="texture"
>
<File
RelativePath=".\crn_dxt_image.cpp"
>
</File>
<File
RelativePath=".\crn_dxt_image.h"
>
</File>
<File
RelativePath=".\crn_ktx_texture.cpp"
>
</File>
<File
RelativePath=".\crn_ktx_texture.h"
>
</File>
<File
RelativePath=".\crn_mipmapped_texture.cpp"
>
</File>
<File
RelativePath=".\crn_mipmapped_texture.h"
>
</File>
</Filter>
</Filter> </Filter>
</Files> </Files>
<Globals> <Globals>
+15 -2
View File
@@ -70,8 +70,6 @@
<Unit filename="crn_data_stream_serializer.h" /> <Unit filename="crn_data_stream_serializer.h" />
<Unit filename="crn_dds_comp.cpp" /> <Unit filename="crn_dds_comp.cpp" />
<Unit filename="crn_dds_comp.h" /> <Unit filename="crn_dds_comp.h" />
<Unit filename="crn_dds_texture.cpp" />
<Unit filename="crn_dds_texture.h" />
<Unit filename="crn_decomp.cpp" /> <Unit filename="crn_decomp.cpp" />
<Unit filename="crn_dxt.cpp" /> <Unit filename="crn_dxt.cpp" />
<Unit filename="crn_dxt.h" /> <Unit filename="crn_dxt.h" />
@@ -92,6 +90,8 @@
<Unit filename="crn_dynamic_stream.h" /> <Unit filename="crn_dynamic_stream.h" />
<Unit filename="crn_dynamic_string.cpp" /> <Unit filename="crn_dynamic_string.cpp" />
<Unit filename="crn_dynamic_string.h" /> <Unit filename="crn_dynamic_string.h" />
<Unit filename="crn_etc.cpp" />
<Unit filename="crn_etc.h" />
<Unit filename="crn_file_utils.cpp" /> <Unit filename="crn_file_utils.cpp" />
<Unit filename="crn_file_utils.h" /> <Unit filename="crn_file_utils.h" />
<Unit filename="crn_find_files.cpp" /> <Unit filename="crn_find_files.cpp" />
@@ -108,6 +108,12 @@
<Unit filename="crn_image_utils.cpp" /> <Unit filename="crn_image_utils.cpp" />
<Unit filename="crn_image_utils.h" /> <Unit filename="crn_image_utils.h" />
<Unit filename="crn_intersect.h" /> <Unit filename="crn_intersect.h" />
<Unit filename="crn_jpgd.cpp" />
<Unit filename="crn_jpgd.h" />
<Unit filename="crn_jpge.cpp" />
<Unit filename="crn_jpge.h" />
<Unit filename="crn_ktx_texture.cpp" />
<Unit filename="crn_ktx_texture.h" />
<Unit filename="crn_lzma_codec.cpp" /> <Unit filename="crn_lzma_codec.cpp" />
<Unit filename="crn_lzma_codec.h" /> <Unit filename="crn_lzma_codec.h" />
<Unit filename="crn_math.cpp" /> <Unit filename="crn_math.cpp" />
@@ -115,6 +121,10 @@
<Unit filename="crn_matrix.h" /> <Unit filename="crn_matrix.h" />
<Unit filename="crn_mem.cpp" /> <Unit filename="crn_mem.cpp" />
<Unit filename="crn_mem.h" /> <Unit filename="crn_mem.h" />
<Unit filename="crn_miniz.cpp" />
<Unit filename="crn_miniz.h" />
<Unit filename="crn_mipmapped_texture.cpp" />
<Unit filename="crn_mipmapped_texture.h" />
<Unit filename="crn_mutex.h" /> <Unit filename="crn_mutex.h" />
<Unit filename="crn_packed_uint.h" /> <Unit filename="crn_packed_uint.h" />
<Unit filename="crn_pixel_format.cpp" /> <Unit filename="crn_pixel_format.cpp" />
@@ -135,6 +145,8 @@
<Unit filename="crn_resample_filters.h" /> <Unit filename="crn_resample_filters.h" />
<Unit filename="crn_resampler.cpp" /> <Unit filename="crn_resampler.cpp" />
<Unit filename="crn_resampler.h" /> <Unit filename="crn_resampler.h" />
<Unit filename="crn_rg_etc1.cpp" />
<Unit filename="crn_rg_etc1.h" />
<Unit filename="crn_ryg_dxt.cpp" /> <Unit filename="crn_ryg_dxt.cpp" />
<Unit filename="crn_ryg_dxt.hpp" /> <Unit filename="crn_ryg_dxt.hpp" />
<Unit filename="crn_ryg_types.hpp" /> <Unit filename="crn_ryg_types.hpp" />
@@ -173,6 +185,7 @@
<Unit filename="crn_vec_interval.h" /> <Unit filename="crn_vec_interval.h" />
<Unit filename="crn_vector.cpp" /> <Unit filename="crn_vector.cpp" />
<Unit filename="crn_vector.h" /> <Unit filename="crn_vector.h" />
<Unit filename="crn_vector2d.h" />
<Unit filename="crn_winhdr.h" /> <Unit filename="crn_winhdr.h" />
<Unit filename="crn_zeng.cpp" /> <Unit filename="crn_zeng.cpp" />
<Unit filename="crn_zeng.h" /> <Unit filename="crn_zeng.h" />
+136 -9
View File
@@ -7,10 +7,13 @@
#include "crn_dynamic_stream.h" #include "crn_dynamic_stream.h"
#include "crn_buffer_stream.h" #include "crn_buffer_stream.h"
#include "crn_ryg_dxt.hpp" #include "crn_ryg_dxt.hpp"
#include "crn_etc.h"
#define CRND_HEADER_FILE_ONLY #define CRND_HEADER_FILE_ONLY
#include "../inc/crn_decomp.h" #include "../inc/crn_decomp.h"
#include "crn_rg_etc1.h"
namespace crnlib namespace crnlib
{ {
static void* realloc_func(void* p, size_t size, size_t* pActual_size, bool movable, void* pUser_data) static void* realloc_func(void* p, size_t size, size_t* pActual_size, bool movable, void* pUser_data)
@@ -31,13 +34,17 @@ namespace crnlib
crnlib_global_initializer() crnlib_global_initializer()
{ {
crn_threading_init(); crn_threading_init();
ryg_dxt::sInitDXT();
crnlib_enable_fail_exceptions(true); crnlib_enable_fail_exceptions(true);
// Redirect crn_decomp.h's memory allocations into crnlib, which may be further redirected by the outside caller. // Redirect crn_decomp.h's memory allocations into crnlib, which may be further redirected by the outside caller.
crnd::crnd_set_memory_callbacks(realloc_func, msize_func, NULL); crnd::crnd_set_memory_callbacks(realloc_func, msize_func, NULL);
ryg_dxt::sInitDXT();
pack_etc1_block_init();
rg_etc1::pack_etc1_block_init();
} }
}; };
@@ -194,8 +201,8 @@ void *crn_compress(const crn_comp_params &comp_params, const crn_mipmap_params &
void *crn_decompress_crn_to_dds(const void *pCRN_file_data, crn_uint32 &file_size) void *crn_decompress_crn_to_dds(const void *pCRN_file_data, crn_uint32 &file_size)
{ {
dds_texture tex; mipmapped_texture tex;
if (!tex.load_crn_from_memory("from_memory.crn", pCRN_file_data, file_size)) if (!tex.read_crn_from_memory(pCRN_file_data, file_size, "from_memory.crn"))
{ {
file_size = 0; file_size = 0;
return NULL; return NULL;
@@ -218,7 +225,7 @@ bool crn_decompress_dds_to_images(const void *pDDS_file_data, crn_uint32 dds_fil
{ {
memset(&tex_desc, 0, sizeof(tex_desc)); memset(&tex_desc, 0, sizeof(tex_desc));
dds_texture tex; mipmapped_texture tex;
buffer_stream in_stream(pDDS_file_data, dds_file_size); buffer_stream in_stream(pDDS_file_data, dds_file_size);
data_stream_serializer in_serializer(in_stream); data_stream_serializer in_serializer(in_stream);
if (!tex.read_dds(in_serializer)) if (!tex.read_dds(in_serializer))
@@ -295,7 +302,7 @@ namespace crnlib
{ {
if (m_image.is_valid()) if (m_image.is_valid())
{ {
m_image.set_block_pixels(0, 0, reinterpret_cast<const color_quad_u8 *>(pPixels), m_pack_params, m_dxt1_optimizer, m_dxt5_optimizer); m_image.set_block_pixels(0, 0, reinterpret_cast<const color_quad_u8 *>(pPixels), m_pack_params, m_set_block_pixels_context);
memcpy(pDst_block, &m_image.get_element(0, 0, 0), m_image.get_bytes_per_block()); memcpy(pDst_block, &m_image.get_element(0, 0, 0), m_image.get_bytes_per_block());
} }
} }
@@ -304,8 +311,7 @@ namespace crnlib
dxt_image m_image; dxt_image m_image;
crn_comp_params m_comp_params; crn_comp_params m_comp_params;
dxt_image::pack_params m_pack_params; dxt_image::pack_params m_pack_params;
dxt1_endpoint_optimizer m_dxt1_optimizer; dxt_image::set_block_pixels_context m_set_block_pixels_context;
dxt5_endpoint_optimizer m_dxt5_optimizer;
}; };
} }
@@ -330,3 +336,124 @@ void crn_free_block_compressor(crn_block_compressor_context_t pContext)
{ {
crnlib_delete(static_cast<crn_block_compressor *>(pContext)); crnlib_delete(static_cast<crn_block_compressor *>(pContext));
} }
bool crn_decompress_block(const void *pSrc_block, crn_uint32 *pDst_pixels_u32, crn_format crn_fmt)
{
color_quad_u8* pDst_pixels = reinterpret_cast<color_quad_u8*>(pDst_pixels_u32);
switch (crn_get_fundamental_dxt_format(crn_fmt))
{
case cCRNFmtETC1:
{
const etc1_block& block = *reinterpret_cast<const etc1_block*>(pSrc_block);
unpack_etc1(block, pDst_pixels, false);
break;
}
case cCRNFmtDXT1:
{
const dxt1_block* pDXT1_block = reinterpret_cast<const dxt1_block*>(pSrc_block);
color_quad_u8 colors[cDXT1SelectorValues];
pDXT1_block->get_block_colors(colors, static_cast<uint16>(pDXT1_block->get_low_color()), static_cast<uint16>(pDXT1_block->get_high_color()));
for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
{
const uint s = pDXT1_block->get_selector(i & 3, i >> 2);
pDst_pixels[i] = colors[s];
}
break;
}
case cCRNFmtDXT3:
{
const dxt3_block* pDXT3_block = reinterpret_cast<const dxt3_block*>(pSrc_block);
const dxt1_block* pDXT1_block = reinterpret_cast<const dxt1_block*>(pSrc_block) + 1;
color_quad_u8 colors[cDXT1SelectorValues];
pDXT1_block->get_block_colors(colors, static_cast<uint16>(pDXT1_block->get_low_color()), static_cast<uint16>(pDXT1_block->get_high_color()));
for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
{
const uint s = pDXT1_block->get_selector(i & 3, i >> 2);
const uint a = pDXT3_block->get_alpha(i & 3, i >> 2, true);
pDst_pixels[i] = colors[s];
pDst_pixels[i].a = static_cast<uint8>(a);
}
break;
}
case cCRNFmtDXT5:
{
const dxt5_block* pDXT5_block = reinterpret_cast<const dxt5_block*>(pSrc_block);
const dxt1_block* pDXT1_block = reinterpret_cast<const dxt1_block*>(pSrc_block) + 1;
color_quad_u8 colors[cDXT1SelectorValues];
pDXT1_block->get_block_colors(colors, static_cast<uint16>(pDXT1_block->get_low_color()), static_cast<uint16>(pDXT1_block->get_high_color()));
uint values[cDXT5SelectorValues];
dxt5_block::get_block_values(values, pDXT5_block->get_low_alpha(), pDXT5_block->get_high_alpha());
for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
{
const uint s = pDXT1_block->get_selector(i & 3, i >> 2);
const uint a = pDXT5_block->get_selector(i & 3, i >> 2);
pDst_pixels[i] = colors[s];
pDst_pixels[i].a = static_cast<uint8>(values[a]);
}
}
case cCRNFmtDXN_XY:
case cCRNFmtDXN_YX:
{
const dxt5_block* pDXT5_block0 = reinterpret_cast<const dxt5_block*>(pSrc_block);
const dxt5_block* pDXT5_block1 = reinterpret_cast<const dxt5_block*>(pSrc_block) + 1;
uint values0[cDXT5SelectorValues];
dxt5_block::get_block_values(values0, pDXT5_block0->get_low_alpha(), pDXT5_block0->get_high_alpha());
uint values1[cDXT5SelectorValues];
dxt5_block::get_block_values(values1, pDXT5_block1->get_low_alpha(), pDXT5_block1->get_high_alpha());
for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
{
const uint s0 = pDXT5_block0->get_selector(i & 3, i >> 2);
const uint s1 = pDXT5_block1->get_selector(i & 3, i >> 2);
if (crn_fmt == cCRNFmtDXN_XY)
pDst_pixels[i].set_noclamp_rgba(values0[s0], values1[s1], 255, 255);
else
pDst_pixels[i].set_noclamp_rgba(values1[s1], values0[s0], 255, 255);
}
break;
}
case cCRNFmtDXT5A:
{
const dxt5_block* pDXT5_block = reinterpret_cast<const dxt5_block*>(pSrc_block);
uint values[cDXT5SelectorValues];
dxt5_block::get_block_values(values, pDXT5_block->get_low_alpha(), pDXT5_block->get_high_alpha());
for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
{
const uint s = pDXT5_block->get_selector(i & 3, i >> 2);
pDst_pixels[i].set_noclamp_rgba(255, 255, 255, values[s]);
}
break;
}
default:
{
return false;
}
}
return true;
}
+23 -6
View File
@@ -16,7 +16,6 @@
<Compiler> <Compiler>
<Add option="-Wextra" /> <Add option="-Wextra" />
<Add option="-Wall" /> <Add option="-Wall" />
<Add option="-g" />
</Compiler> </Compiler>
</Target> </Target>
<Target title="Release"> <Target title="Release">
@@ -27,22 +26,24 @@
<Option compiler="gcc" /> <Option compiler="gcc" />
<Option createDefFile="1" /> <Option createDefFile="1" />
<Compiler> <Compiler>
<Add option="-march=core2" />
<Add option="-fomit-frame-pointer" /> <Add option="-fomit-frame-pointer" />
<Add option="-fexpensive-optimizations" /> <Add option="-fexpensive-optimizations" />
<Add option="-O3" /> <Add option="-O3" />
<Add option="-Wextra" /> <Add option="-Wextra" />
<Add option="-Wall" /> <Add option="-Wall" />
</Compiler> </Compiler>
<Linker>
<Add option="-s" />
</Linker>
</Target> </Target>
</Build> </Build>
<Compiler> <Compiler>
<Add option="-Wextra" /> <Add option="-Wextra" />
<Add option="-Wall" /> <Add option="-Wall" />
<Add option="-g" />
<Add option="-Wno-unused-value" /> <Add option="-Wno-unused-value" />
<Add option="-Wno-unused" /> <Add option="-Wno-unused" />
<Add option="-fno-strict-aliasing" />
<Add option="-ffast-math" />
<Add option="-fno-math-errno" />
</Compiler> </Compiler>
<Unit filename="crn_arealist.cpp" /> <Unit filename="crn_arealist.cpp" />
<Unit filename="crn_arealist.h" /> <Unit filename="crn_arealist.h" />
@@ -71,8 +72,6 @@
<Unit filename="crn_data_stream_serializer.h" /> <Unit filename="crn_data_stream_serializer.h" />
<Unit filename="crn_dds_comp.cpp" /> <Unit filename="crn_dds_comp.cpp" />
<Unit filename="crn_dds_comp.h" /> <Unit filename="crn_dds_comp.h" />
<Unit filename="crn_dds_texture.cpp" />
<Unit filename="crn_dds_texture.h" />
<Unit filename="crn_decomp.cpp" /> <Unit filename="crn_decomp.cpp" />
<Unit filename="crn_dxt.cpp" /> <Unit filename="crn_dxt.cpp" />
<Unit filename="crn_dxt.h" /> <Unit filename="crn_dxt.h" />
@@ -93,6 +92,8 @@
<Unit filename="crn_dynamic_stream.h" /> <Unit filename="crn_dynamic_stream.h" />
<Unit filename="crn_dynamic_string.cpp" /> <Unit filename="crn_dynamic_string.cpp" />
<Unit filename="crn_dynamic_string.h" /> <Unit filename="crn_dynamic_string.h" />
<Unit filename="crn_etc.cpp" />
<Unit filename="crn_etc.h" />
<Unit filename="crn_file_utils.cpp" /> <Unit filename="crn_file_utils.cpp" />
<Unit filename="crn_file_utils.h" /> <Unit filename="crn_file_utils.h" />
<Unit filename="crn_find_files.cpp" /> <Unit filename="crn_find_files.cpp" />
@@ -109,6 +110,12 @@
<Unit filename="crn_image_utils.cpp" /> <Unit filename="crn_image_utils.cpp" />
<Unit filename="crn_image_utils.h" /> <Unit filename="crn_image_utils.h" />
<Unit filename="crn_intersect.h" /> <Unit filename="crn_intersect.h" />
<Unit filename="crn_jpgd.cpp" />
<Unit filename="crn_jpgd.h" />
<Unit filename="crn_jpge.cpp" />
<Unit filename="crn_jpge.h" />
<Unit filename="crn_ktx_texture.cpp" />
<Unit filename="crn_ktx_texture.h" />
<Unit filename="crn_lzma_codec.cpp" /> <Unit filename="crn_lzma_codec.cpp" />
<Unit filename="crn_lzma_codec.h" /> <Unit filename="crn_lzma_codec.h" />
<Unit filename="crn_math.cpp" /> <Unit filename="crn_math.cpp" />
@@ -116,6 +123,10 @@
<Unit filename="crn_matrix.h" /> <Unit filename="crn_matrix.h" />
<Unit filename="crn_mem.cpp" /> <Unit filename="crn_mem.cpp" />
<Unit filename="crn_mem.h" /> <Unit filename="crn_mem.h" />
<Unit filename="crn_miniz.cpp" />
<Unit filename="crn_miniz.h" />
<Unit filename="crn_mipmapped_texture.cpp" />
<Unit filename="crn_mipmapped_texture.h" />
<Unit filename="crn_mutex.h" /> <Unit filename="crn_mutex.h" />
<Unit filename="crn_packed_uint.h" /> <Unit filename="crn_packed_uint.h" />
<Unit filename="crn_pixel_format.cpp" /> <Unit filename="crn_pixel_format.cpp" />
@@ -136,6 +147,8 @@
<Unit filename="crn_resample_filters.h" /> <Unit filename="crn_resample_filters.h" />
<Unit filename="crn_resampler.cpp" /> <Unit filename="crn_resampler.cpp" />
<Unit filename="crn_resampler.h" /> <Unit filename="crn_resampler.h" />
<Unit filename="crn_rg_etc1.cpp" />
<Unit filename="crn_rg_etc1.h" />
<Unit filename="crn_ryg_dxt.cpp" /> <Unit filename="crn_ryg_dxt.cpp" />
<Unit filename="crn_ryg_dxt.hpp" /> <Unit filename="crn_ryg_dxt.hpp" />
<Unit filename="crn_ryg_types.hpp" /> <Unit filename="crn_ryg_types.hpp" />
@@ -174,6 +187,7 @@
<Unit filename="crn_vec_interval.h" /> <Unit filename="crn_vec_interval.h" />
<Unit filename="crn_vector.cpp" /> <Unit filename="crn_vector.cpp" />
<Unit filename="crn_vector.h" /> <Unit filename="crn_vector.h" />
<Unit filename="crn_vector2d.h" />
<Unit filename="crn_winhdr.h" /> <Unit filename="crn_winhdr.h" />
<Unit filename="crn_zeng.cpp" /> <Unit filename="crn_zeng.cpp" />
<Unit filename="crn_zeng.h" /> <Unit filename="crn_zeng.h" />
@@ -209,6 +223,9 @@
<Unit filename="lzma_MyVersion.h" /> <Unit filename="lzma_MyVersion.h" />
<Unit filename="lzma_Threads.h" /> <Unit filename="lzma_Threads.h" />
<Unit filename="lzma_Types.h" /> <Unit filename="lzma_Types.h" />
<Unit filename="../inc/crn_decomp.h" />
<Unit filename="../inc/crnlib.h" />
<Unit filename="../inc/dds_defs.h" />
<Extensions> <Extensions>
<code_completion /> <code_completion />
<debugger /> <debugger />
+9 -9
View File
@@ -60,7 +60,7 @@ void MtSync_StopWriting(CMtSync *p)
p->csWasEntered = False; p->csWasEntered = False;
} }
Semaphore_Release1(&p->freeSemaphore); Semaphore_Release1(&p->freeSemaphore);
Event_Wait(&p->wasStopped); Event_Wait(&p->wasStopped);
while (myNumBlocks++ != p->numProcessedBlocks) while (myNumBlocks++ != p->numProcessedBlocks)
@@ -110,12 +110,12 @@ static SRes MtSync_Create2(CMtSync *p, unsigned (MY_STD_CALL *startAddress)(void
RINOK_THREAD(AutoResetEvent_CreateNotSignaled(&p->canStart)); RINOK_THREAD(AutoResetEvent_CreateNotSignaled(&p->canStart));
RINOK_THREAD(AutoResetEvent_CreateNotSignaled(&p->wasStarted)); RINOK_THREAD(AutoResetEvent_CreateNotSignaled(&p->wasStarted));
RINOK_THREAD(AutoResetEvent_CreateNotSignaled(&p->wasStopped)); RINOK_THREAD(AutoResetEvent_CreateNotSignaled(&p->wasStopped));
RINOK_THREAD(Semaphore_Create(&p->freeSemaphore, numBlocks, numBlocks)); RINOK_THREAD(Semaphore_Create(&p->freeSemaphore, numBlocks, numBlocks));
RINOK_THREAD(Semaphore_Create(&p->filledSemaphore, 0, numBlocks)); RINOK_THREAD(Semaphore_Create(&p->filledSemaphore, 0, numBlocks));
p->needStart = True; p->needStart = True;
RINOK_THREAD(Thread_Create(&p->thread, startAddress, obj)); RINOK_THREAD(Thread_Create(&p->thread, startAddress, obj));
p->wasCreated = True; p->wasCreated = True;
return SZ_OK; return SZ_OK;
@@ -387,7 +387,7 @@ void BtFillBlock(CMatchFinderMt *p, UInt32 globalBlockIndex)
CriticalSection_Enter(&sync->cs); CriticalSection_Enter(&sync->cs);
sync->csWasEntered = True; sync->csWasEntered = True;
} }
BtGetMatches(p, p->btBuf + (globalBlockIndex & kMtBtNumBlocksMask) * kMtBtBlockSize); BtGetMatches(p, p->btBuf + (globalBlockIndex & kMtBtNumBlocksMask) * kMtBtBlockSize);
if (p->pos > kMtMaxValForNormalize - kMtBtBlockSize) if (p->pos > kMtMaxValForNormalize - kMtBtBlockSize)
@@ -456,7 +456,7 @@ void MatchFinderMt_Destruct(CMatchFinderMt *p, ISzAlloc *alloc)
static unsigned MY_STD_CALL HashThreadFunc2(void *p) { HashThreadFunc((CMatchFinderMt *)p); return 0; } static unsigned MY_STD_CALL HashThreadFunc2(void *p) { HashThreadFunc((CMatchFinderMt *)p); return 0; }
static unsigned MY_STD_CALL BtThreadFunc2(void *p) static unsigned MY_STD_CALL BtThreadFunc2(void *p)
{ {
Byte allocaDummy[0x180]; Byte allocaDummy[0x180]; (void)allocaDummy;
int i = 0; int i = 0;
for (i = 0; i < 16; i++) for (i = 0; i < 16; i++)
allocaDummy[i] = (Byte)i; allocaDummy[i] = (Byte)i;
@@ -563,7 +563,7 @@ UInt32 * MixMatches2(CMatchFinderMt *p, UInt32 matchMinPos, UInt32 *distances)
const Byte *cur = p->pointerToCurPos; const Byte *cur = p->pointerToCurPos;
UInt32 lzPos = p->lzPos; UInt32 lzPos = p->lzPos;
MT_HASH2_CALC MT_HASH2_CALC
curMatch2 = hash[hash2Value]; curMatch2 = hash[hash2Value];
hash[hash2Value] = lzPos; hash[hash2Value] = lzPos;
@@ -586,7 +586,7 @@ UInt32 * MixMatches3(CMatchFinderMt *p, UInt32 matchMinPos, UInt32 *distances)
curMatch2 = hash[ hash2Value]; curMatch2 = hash[ hash2Value];
curMatch3 = hash[kFix3HashSize + hash3Value]; curMatch3 = hash[kFix3HashSize + hash3Value];
hash[ hash2Value] = hash[ hash2Value] =
hash[kFix3HashSize + hash3Value] = hash[kFix3HashSize + hash3Value] =
lzPos; lzPos;
@@ -618,11 +618,11 @@ UInt32 *MixMatches4(CMatchFinderMt *p, UInt32 matchMinPos, UInt32 *distances)
const Byte *cur = p->pointerToCurPos; const Byte *cur = p->pointerToCurPos;
UInt32 lzPos = p->lzPos; UInt32 lzPos = p->lzPos;
MT_HASH4_CALC MT_HASH4_CALC
curMatch2 = hash[ hash2Value]; curMatch2 = hash[ hash2Value];
curMatch3 = hash[kFix3HashSize + hash3Value]; curMatch3 = hash[kFix3HashSize + hash3Value];
curMatch4 = hash[kFix4HashSize + hash4Value]; curMatch4 = hash[kFix4HashSize + hash4Value];
hash[ hash2Value] = hash[ hash2Value] =
hash[kFix3HashSize + hash3Value] = hash[kFix3HashSize + hash3Value] =
hash[kFix4HashSize + hash4Value] = hash[kFix4HashSize + hash4Value] =
+1 -1
View File
@@ -2176,7 +2176,7 @@ SRes LzmaEnc_Encode(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *i
SRes res = SZ_OK; SRes res = SZ_OK;
#ifdef COMPRESS_MF_MT #ifdef COMPRESS_MF_MT
Byte allocaDummy[0x300]; Byte allocaDummy[0x300]; (void)allocaDummy;
int i = 0; int i = 0;
for (i = 0; i < 16; i++) for (i = 0; i < 16; i++)
allocaDummy[i] = (Byte)i; allocaDummy[i] = (Byte)i;
+357
View File
@@ -0,0 +1,357 @@
// File: corpus_gen.cpp - Block compression corpus generator.
// See Copyright Notice and license at the end of inc/crnlib.h
//
// Example command line:
// -gentest [-deep] [-blockpercentage .035] [-width 4096] [-height 4096] -in c:\temp\*.jpg [-in c:\temp\*.jpeg] [-in @blah.txt]
#include "crn_core.h"
#include "corpus_gen.h"
#include "crn_console.h"
#include "crn_find_files.h"
#include "crn_file_utils.h"
#include "crn_command_line_params.h"
#include "crn_dxt.h"
#include "crn_cfile_stream.h"
#include "crn_texture_conversion.h"
#include "crn_radix_sort.h"
#define CRND_HEADER_FILE_ONLY
#include "crn_decomp.h"
namespace crnlib
{
struct block
{
color_quad_u8 m_c[4*4];
inline operator size_t() const { return fast_hash(this, sizeof(*this)); }
inline bool operator== (const block& rhs) const
{
return memcmp(this, &rhs, sizeof(*this)) == 0;
}
};
typedef crnlib::hash_map<block, empty_type> block_hash_map;
corpus_gen::corpus_gen()
{
}
void corpus_gen::sort_blocks(image_u8& img)
{
const uint num_blocks_x = img.get_width() / 4;
const uint num_blocks_y = img.get_height() / 4;
const uint total_blocks = num_blocks_x * num_blocks_y;
console::printf("Sorting %u blocks...", total_blocks);
crnlib::vector<float> block_std_dev(total_blocks);
for (uint by = 0; by < num_blocks_y; by++)
{
for (uint bx = 0; bx < num_blocks_x; bx++)
{
color_quad_u8 c[4 * 4];
for (uint y = 0; y < 4; y++)
for (uint x = 0; x < 4; x++)
c[x+y*4] = img(bx*4+x, by*4+y);
double std_dev = 0.0f;
for (uint i = 0; i < 3; i++)
std_dev += image_utils::compute_std_dev(16, c, i, 1);
block_std_dev[bx + by * num_blocks_x] = (float)std_dev;
}
}
crnlib::vector<uint> block_indices0(total_blocks);
crnlib::vector<uint> block_indices1(total_blocks);
const uint* pIndices = indirect_radix_sort(total_blocks, &block_indices0[0], &block_indices1[0], &block_std_dev[0], 0, sizeof(float), true);
image_u8 new_img(img.get_width(), img.get_height());
uint dst_block_index = 0;
//float prev_std_dev = -999;
for (uint i = 0; i < total_blocks; i++)
{
uint src_block_index = pIndices[i];
//float std_dev = block_std_dev[src_block_index];
//crnlib_ASSERT(std_dev >= prev_std_dev);
//prev_std_dev = std_dev;
uint src_block_x = src_block_index % num_blocks_x;
uint src_block_y = src_block_index / num_blocks_x;
uint dst_block_x = dst_block_index % num_blocks_x;
uint dst_block_y = dst_block_index / num_blocks_x;
new_img.unclipped_blit(src_block_x * 4, src_block_y * 4, 4, 4, dst_block_x * 4, dst_block_y * 4, img);
dst_block_index++;
}
#if 0
//new_img.swap(img);
#else
crnlib::vector<uint> remaining_blocks(num_blocks_x);
console::printf("Arranging %u blocks...", total_blocks);
for (uint by = 0; by < num_blocks_y; by++)
{
console::printf("%u of %u", by, num_blocks_y);
remaining_blocks.resize(num_blocks_x);
for (uint i = 0; i < num_blocks_x; i++)
remaining_blocks[i] = i;
color_quad_u8 match_block[16];
utils::zero_object(match_block);
for (uint bx = 0; bx < num_blocks_x; bx++)
{
uint best_index = 0;
uint64 best_error = cUINT64_MAX;
for (uint i = 0; i < remaining_blocks.size(); i++)
{
uint src_block_index = remaining_blocks[i];
uint64 error = 0;
for (uint y = 0; y < 4; y++)
{
for (uint x = 0; x < 4; x++)
{
const color_quad_u8& c = new_img(src_block_index*4+x, by*4+y);
error += color::elucidian_distance(c, match_block[x+y*4], false);
}
}
if (error < best_error)
{
best_error = error;
best_index = i;
}
}
uint src_block_index = remaining_blocks[best_index];
for (uint y = 0; y < 4; y++)
{
for (uint x = 0; x < 4; x++)
{
const color_quad_u8& c = new_img(src_block_index*4+x, by*4+y);
match_block[x+y*4] = c;
img(bx * 4 + x, by * 4 + y) = c;
}
}
remaining_blocks.erase_unordered(best_index);
}
}
#endif
}
bool corpus_gen::generate(const char* pCmd_line)
{
static const command_line_params::param_desc param_desc_array[] =
{
{ "corpus_gen", 0, false },
{ "in", 1, true },
{ "deep", 0, false },
{ "blockpercentage", 1, false },
{ "width", 1, false },
{ "height", 1, false },
{ "alpha", 0, false },
};
command_line_params params;
if (!params.parse(pCmd_line, CRNLIB_ARRAY_SIZE(param_desc_array), param_desc_array, true))
return false;
if (!params.has_key("in"))
{
console::error("Must specify one or more input files using the /in option!");
return false;
}
uint num_dst_blocks_x = params.get_value_as_int("width", 0, 4096, 128, 4096);
num_dst_blocks_x = (num_dst_blocks_x + 3) / 4;
uint num_dst_blocks_y = params.get_value_as_int("height", 0, 4096, 128, 4096);
num_dst_blocks_y = (num_dst_blocks_y + 3) / 4;
const uint total_dst_blocks = num_dst_blocks_x * num_dst_blocks_y;
image_u8 dst_img(num_dst_blocks_x * 4, num_dst_blocks_y * 4);
uint next_dst_block = 0;
uint total_dst_images = 0;
random rm;
block_hash_map block_hash;
block_hash.reserve(total_dst_blocks);
uint total_images_loaded = 0;
uint total_blocks_written = 0;
command_line_params::param_map_const_iterator it = params.begin();
for ( ; it != params.end(); ++it)
{
if (it->first != "in")
continue;
if (it->second.m_values.empty())
{
console::error("Must follow /in parameter with a filename!\n");
return false;
}
for (uint in_value_index = 0; in_value_index < it->second.m_values.size(); in_value_index++)
{
const dynamic_string& filespec = it->second.m_values[in_value_index];
find_files file_finder;
if (!file_finder.find(filespec.get_ptr(), find_files::cFlagAllowFiles | (params.has_key("deep") ? find_files::cFlagRecursive : 0)))
{
console::warning("Failed finding files: %s", filespec.get_ptr());
continue;
}
if (file_finder.get_files().empty())
{
console::warning("No files found: %s", filespec.get_ptr());
return false;
}
const find_files::file_desc_vec& files = file_finder.get_files();
for (uint file_index = 0; file_index < files.size(); file_index++)
{
const find_files::file_desc& file_desc = files[file_index];
console::printf("Loading image: %s", file_desc.m_fullname.get_ptr());
image_u8 img;
if (!image_utils::read_from_file(img, file_desc.m_fullname.get_ptr(), 0))
{
console::warning("Failed loading image file: %s", file_desc.m_fullname.get_ptr());
continue;
}
if (!params.has_key("alpha"))
{
for (uint y = 0; y < img.get_height(); y++)
for (uint x = 0; x < img.get_width(); x++)
img(x, y).a = 255;
}
total_images_loaded++;
uint width = img.get_width();
uint height = img.get_height();
uint num_blocks_x = (width + 3) / 4;
uint num_blocks_y = (height + 3) / 4;
uint total_blocks = num_blocks_x * num_blocks_y;
float percentage = params.get_value_as_float("blockpercentage", 0, .1f, .001f, 1.0f);
uint total_rand_blocks = math::maximum<uint>(1U, (uint)(total_blocks * percentage));
crnlib::vector<uint> remaining_blocks(total_blocks);
for (uint i = 0; i < total_blocks; i++)
remaining_blocks[i] = i;
uint num_blocks_remaining = total_rand_blocks;
while (num_blocks_remaining)
{
if (remaining_blocks.empty())
break;
uint rand_block_index = rm.irand(0, remaining_blocks.size());
uint block_index = remaining_blocks[rand_block_index];
remaining_blocks.erase_unordered(rand_block_index);
uint block_y = block_index / num_blocks_x;
uint block_x = block_index % num_blocks_x;
block b;
for (uint y = 0; y < 4; y++)
{
for (uint x = 0; x < 4; x++)
{
b.m_c[x+y*4] = img.get_clamped(block_x*4+x, block_y*4+y);
}
}
if (!block_hash.insert(b).second)
continue;
if (block_hash.size() == total_dst_blocks)
{
block_hash.clear();
block_hash.reserve(total_dst_blocks);
}
uint dst_block_x = next_dst_block % num_dst_blocks_x;
uint dst_block_y = next_dst_block / num_dst_blocks_x;
for (uint y = 0; y < 4; y++)
{
for (uint x = 0; x < 4; x++)
{
dst_img(dst_block_x * 4 + x, dst_block_y * 4 + y) = b.m_c[x + y*4];
}
}
next_dst_block++;
if (total_dst_blocks == next_dst_block)
{
sort_blocks(dst_img);
dynamic_string dst_filename(cVarArg, "test_%u.tga", total_dst_images);
console::printf("Writing image: %s", dst_filename.get_ptr());
image_utils::write_to_file(dst_filename.get_ptr(), dst_img, 0);
dst_img.set_all(color_quad_u8::make_black());
next_dst_block = 0;
total_dst_images++;
}
total_blocks_written++;
num_blocks_remaining--;
}
} // file_index
} // in_value_index
}
if (next_dst_block)
{
sort_blocks(dst_img);
dynamic_string dst_filename(cVarArg, "test_%u.tga", total_dst_images);
console::printf("Writing image: %s", dst_filename.get_ptr());
image_utils::write_to_file(dst_filename.get_ptr(), dst_img, 0);
next_dst_block = 0;
total_dst_images++;
}
console::printf("Found %u input images, %u output images, %u total blocks", total_images_loaded, total_dst_images, total_blocks_written);
return true;
}
} // namespace crnlib
+20
View File
@@ -0,0 +1,20 @@
// File: corpus_gen.h
// See Copyright Notice and license at the end of inc/crnlib.h
#pragma once
#include "crn_command_line_params.h"
#include "crn_image.h"
namespace crnlib
{
class corpus_gen
{
public:
corpus_gen();
bool generate(const char* pCmd_line);
private:
void sort_blocks(image_u8& img);
};
} // namespace crnlib
+404
View File
@@ -0,0 +1,404 @@
// File: corpus_test.cpp
#include "crn_core.h"
#include "corpus_test.h"
#include "crn_find_files.h"
#include "crn_console.h"
#include "crn_image_utils.h"
#include "crn_hash.h"
#include "crn_hash_map.h"
#include "crn_radix_sort.h"
#include "crn_mipmapped_texture.h"
namespace crnlib
{
corpus_tester::corpus_tester()
{
m_bad_block_img.resize(256, 256);
m_next_bad_block_index = 0;
m_total_bad_block_files = 0;
}
void corpus_tester::print_comparative_metric_stats(const command_line_params& cmd_line_params, const crnlib::vector<image_utils::error_metrics>& stats1, const crnlib::vector<image_utils::error_metrics>& stats2, uint num_blocks_x, uint num_blocks_y)
{
num_blocks_y;
crnlib::vector<uint> better_blocks;
crnlib::vector<uint> equal_blocks;
crnlib::vector<uint> worse_blocks;
crnlib::vector<float> delta_psnr;
for (uint i = 0; i < stats1.size(); i++)
{
//uint bx = i % num_blocks_x;
//uint by = i / num_blocks_x;
const image_utils::error_metrics& em1 = stats1[i];
const image_utils::error_metrics& em2 = stats2[i];
if (em1.mPeakSNR < em2.mPeakSNR)
{
worse_blocks.push_back(i);
delta_psnr.push_back((float)(em2.mPeakSNR - em1.mPeakSNR));
}
else if (fabs(em1.mPeakSNR - em2.mPeakSNR) < .001f)
equal_blocks.push_back(i);
else
better_blocks.push_back(i);
}
console::printf("Num worse blocks: %u, %3.3f%%", worse_blocks.size(), worse_blocks.size() * 100.0f / stats1.size());
console::printf("Num equal blocks: %u, %3.3f%%", equal_blocks.size(), equal_blocks.size() * 100.0f / stats1.size());
console::printf("Num better blocks: %u, %3.3f%%", better_blocks.size(), better_blocks.size() * 100.0f / stats1.size());
console::printf("Num equal+better blocks: %u, %3.3f%%", equal_blocks.size()+better_blocks.size(), (equal_blocks.size()+better_blocks.size()) * 100.0f / stats1.size());
if (!cmd_line_params.has_key("nobadblocks"))
{
crnlib::vector<uint> indices[2];
indices[0].resize(worse_blocks.size());
indices[1].resize(worse_blocks.size());
uint* pSorted_indices = NULL;
if (worse_blocks.size())
{
pSorted_indices = indirect_radix_sort(worse_blocks.size(), &indices[0][0], &indices[1][0], &delta_psnr[0], 0, sizeof(float), true);
console::printf("List of worse blocks sorted by delta PSNR:");
for (uint i = 0; i < worse_blocks.size(); i++)
{
uint block_index = worse_blocks[pSorted_indices[i]];
uint bx = block_index % num_blocks_x;
uint by = block_index / num_blocks_x;
console::printf("%u. [%u,%u] %3.3f %3.3f %3.3f",
i,
bx, by,
stats1[block_index].mPeakSNR,
stats2[block_index].mPeakSNR,
stats2[block_index].mPeakSNR - stats1[block_index].mPeakSNR);
}
}
}
}
void corpus_tester::print_metric_stats(const crnlib::vector<image_utils::error_metrics>& stats, uint num_blocks_x, uint num_blocks_y)
{
num_blocks_y;
image_utils::error_metrics best_metrics;
image_utils::error_metrics worst_metrics;
worst_metrics.mPeakSNR = 1e+6f;
vec2I best_loc;
vec2I worst_loc;
utils::zero_object(best_loc);
utils::zero_object(worst_loc);
double psnr_total = 0.0f;
double psnr2_total = 0.0f;
uint num_non_inf = 0;
uint num_inf = 0;
for (uint i = 0; i < stats.size(); i++)
{
uint bx = i % num_blocks_x;
uint by = i / num_blocks_x;
const image_utils::error_metrics& em = stats[i];
if ((em.mPeakSNR < 200.0f) && (em > best_metrics)) { best_metrics = em; best_loc.set(bx, by); }
if (em < worst_metrics) { worst_metrics = em; worst_loc.set(bx, by); }
if (em.mPeakSNR < 200.0f)
{
psnr_total += em.mPeakSNR;
psnr2_total += em.mPeakSNR*em.mPeakSNR;
num_non_inf++;
}
else
{
num_inf++;
}
}
console::printf("Number of infinite PSNR blocks: %u", num_inf);
console::printf("Number of non-infinite PSNR blocks: %u", num_non_inf);
if (num_non_inf)
{
psnr_total /= num_non_inf;
psnr2_total /= num_non_inf;
double psnr_std_dev = sqrt(psnr2_total - psnr_total * psnr_total);
console::printf("Average Non-Inf PSNR: %3.3f, Std dev: %3.3f", psnr_total, psnr_std_dev);
console::printf("Worst PSNR: %3.3f, Block Location: %i,%i", worst_metrics.mPeakSNR, worst_loc[0], worst_loc[1]);
console::printf("Best Non-Inf PSNR: %3.3f, Block Location: %i,%i", best_metrics.mPeakSNR, best_loc[0], best_loc[1]);
}
}
void corpus_tester::flush_bad_blocks()
{
if (!m_next_bad_block_index)
return;
dynamic_string filename(cVarArg, "badblocks_%u.tga", m_total_bad_block_files);
console::printf("Writing bad block image: %s", filename.get_ptr());
image_utils::write_to_file(filename.get_ptr(), m_bad_block_img, image_utils::cWriteFlagIgnoreAlpha);
m_bad_block_img.set_all(color_quad_u8::make_black());
m_total_bad_block_files++;
m_next_bad_block_index = 0;
}
void corpus_tester::add_bad_block(image_u8& block)
{
uint num_blocks_x = m_bad_block_img.get_block_width(4);
uint num_blocks_y = m_bad_block_img.get_block_height(4);
uint total_blocks = num_blocks_x * num_blocks_y;
m_bad_block_img.blit((m_next_bad_block_index % num_blocks_x) * 4, (m_next_bad_block_index / num_blocks_x) * 4, block);
m_next_bad_block_index++;
if (m_next_bad_block_index == total_blocks)
flush_bad_blocks();
}
static bool progress_callback(uint percentage_complete, void* pUser_data_ptr)
{
static int s_prev_percentage_complete = -1;
pUser_data_ptr;
if (s_prev_percentage_complete != static_cast<int>(percentage_complete))
{
console::progress("%u%%", percentage_complete);
s_prev_percentage_complete = percentage_complete;
}
return true;
}
bool corpus_tester::test(const char* pCmd_line)
{
console::printf("Command line:\n\"%s\"", pCmd_line);
static const command_line_params::param_desc param_desc_array[] =
{
{ "corpus_test", 0, false },
{ "in", 1, true },
{ "deep", 0, false },
{ "alpha", 0, false },
{ "nomips", 0, false },
{ "perceptual", 0, false },
{ "endpointcaching", 0, false },
{ "multithreaded", 0, false },
{ "writehybrid", 0, false },
{ "nobadblocks", 0, false },
};
command_line_params cmd_line_params;
if (!cmd_line_params.parse(pCmd_line, CRNLIB_ARRAY_SIZE(param_desc_array), param_desc_array, true))
return false;
double total_time1 = 0, total_time2 = 0;
command_line_params::param_map_const_iterator it = cmd_line_params.begin();
for ( ; it != cmd_line_params.end(); ++it)
{
if (it->first != "in")
continue;
if (it->second.m_values.empty())
{
console::error("Must follow /in parameter with a filename!\n");
return false;
}
for (uint in_value_index = 0; in_value_index < it->second.m_values.size(); in_value_index++)
{
const dynamic_string& filespec = it->second.m_values[in_value_index];
find_files file_finder;
if (!file_finder.find(filespec.get_ptr(), find_files::cFlagAllowFiles | (cmd_line_params.has_key("deep") ? find_files::cFlagRecursive : 0)))
{
console::warning("Failed finding files: %s", filespec.get_ptr());
continue;
}
if (file_finder.get_files().empty())
{
console::warning("No files found: %s", filespec.get_ptr());
return false;
}
const find_files::file_desc_vec& files = file_finder.get_files();
image_u8 o(4, 4), a(4, 4), b(4, 4);
uint first_channel = 0;
uint num_channels = 3;
bool perceptual = cmd_line_params.get_value_as_bool("perceptual", false);
if (perceptual)
{
first_channel = 0;
num_channels = 0;
}
console::printf("Perceptual mode: %u", perceptual);
for (uint file_index = 0; file_index < files.size(); file_index++)
{
const find_files::file_desc& file_desc = files[file_index];
console::printf("-------- Loading image: %s", file_desc.m_fullname.get_ptr());
image_u8 img;
if (!image_utils::read_from_file(img, file_desc.m_fullname.get_ptr(), 0))
{
console::warning("Failed loading image file: %s", file_desc.m_fullname.get_ptr());
continue;
}
if ((!cmd_line_params.has_key("alpha")) && img.is_component_valid(3))
{
for (uint y = 0; y < img.get_height(); y++)
for (uint x = 0; x < img.get_width(); x++)
img(x, y).a = 255;
img.set_component_valid(3, false);
}
mipmapped_texture orig_tex;
orig_tex.assign(crnlib_new<image_u8>(img));
if (!cmd_line_params.has_key("nomips"))
{
mipmapped_texture::generate_mipmap_params genmip_params;
genmip_params.m_srgb = true;
console::printf("Generating mipmaps");
if (!orig_tex.generate_mipmaps(genmip_params, false))
{
console::error("Mipmap generation failed!");
return false;
}
}
console::printf("Compress 1");
mipmapped_texture tex1(orig_tex);
dxt_image::pack_params convert_params;
convert_params.m_endpoint_caching = cmd_line_params.get_value_as_bool("endpointcaching", 0, false);
convert_params.m_compressor = cCRNDXTCompressorCRN;
convert_params.m_quality = cCRNDXTQualityNormal;
convert_params.m_perceptual = perceptual;
convert_params.m_num_helper_threads = cmd_line_params.get_value_as_bool("multithreaded", 0, true) ? (g_number_of_processors - 1) : 0;
convert_params.m_pProgress_callback = progress_callback;
timer t;
t.start();
if (!tex1.convert(PIXEL_FMT_ETC1, false, convert_params))
{
console::error("Texture conversion failed!");
return false;
}
double time1 = t.get_elapsed_secs();
total_time1 += time1;
console::printf("Elapsed time: %3.3f", time1);
console::printf("Compress 2");
mipmapped_texture tex2(orig_tex);
convert_params.m_endpoint_caching = false;
convert_params.m_compressor = cCRNDXTCompressorCRN;
convert_params.m_quality = cCRNDXTQualitySuperFast;
t.start();
if (!tex2.convert(PIXEL_FMT_ETC1, false, convert_params))
{
console::error("Texture conversion failed!");
return false;
}
double time2 = t.get_elapsed_secs();
total_time2 += time2;
console::printf("Elapsed time: %3.3f", time2);
image_u8 hybrid_img(img.get_width(), img.get_height());
for (uint l = 0; l < orig_tex.get_num_levels(); l++)
{
image_u8 orig_img, img1, img2;
image_u8* pOrig = orig_tex.get_level(0, l)->get_unpacked_image(orig_img, cUnpackFlagUncook | cUnpackFlagUnflip);
image_u8* pImg1 = tex1.get_level(0, l)->get_unpacked_image(img1, cUnpackFlagUncook | cUnpackFlagUnflip);
image_u8* pImg2 = tex2.get_level(0, l)->get_unpacked_image(img2, cUnpackFlagUncook | cUnpackFlagUnflip);
const uint num_blocks_x = pOrig->get_block_width(4);
const uint num_blocks_y = pOrig->get_block_height(4);
crnlib::vector<image_utils::error_metrics> metrics[2];
for (uint by = 0; by < num_blocks_y; by++)
{
for (uint bx = 0; bx < num_blocks_x; bx++)
{
pOrig->extract_block(o.get_ptr(), bx * 4, by * 4, 4, 4);
pImg1->extract_block(a.get_ptr(), bx * 4, by * 4, 4, 4);
pImg2->extract_block(b.get_ptr(), bx * 4, by * 4, 4, 4);
image_utils::error_metrics em1;
em1.compute(o, a, first_channel, num_channels);
image_utils::error_metrics em2;
em2.compute(o, b, first_channel, num_channels);
metrics[0].push_back(em1);
metrics[1].push_back(em2);
if (em1.mPeakSNR < em2.mPeakSNR)
{
add_bad_block(o);
hybrid_img.blit(bx * 4, by * 4, b);
}
else
{
hybrid_img.blit(bx * 4, by * 4, a);
}
}
}
if (cmd_line_params.has_key("writehybrid"))
image_utils::write_to_file("hybrid.tga", hybrid_img, image_utils::cWriteFlagIgnoreAlpha);
console::printf("---- Mip level: %u, Total blocks: %ux%u, %u", l, num_blocks_x, num_blocks_y, num_blocks_x * num_blocks_y);
console::printf("Compressor 1:");
print_metric_stats(metrics[0], num_blocks_x, num_blocks_y);
console::printf("Compressor 2:");
print_metric_stats(metrics[1], num_blocks_x, num_blocks_y);
console::printf("Compressor 1 vs. 2:");
print_comparative_metric_stats(cmd_line_params, metrics[0], metrics[1], num_blocks_x, num_blocks_y);
image_utils::error_metrics em;
em.compute(*pOrig, *pImg1, 0, perceptual ? 0 : 3);
em.print("Compressor 1: ");
em.compute(*pOrig, *pImg2, 0, perceptual ? 0 : 3);
em.print("Compressor 2: ");
em.compute(*pOrig, hybrid_img, 0, perceptual ? 0 : 3);
em.print("Best of Both: ");
}
}
} // file_index
}
flush_bad_blocks();
console::printf("Total times: %4.3f vs. %4.3f", total_time1, total_time2);
return true;
}
} // namespace crnlib
+28
View File
@@ -0,0 +1,28 @@
// File: corpus_test.h
// See Copyright Notice and license at the end of inc/crnlib.h
#pragma once
#include "crn_command_line_params.h"
#include "crn_image_utils.h"
namespace crnlib
{
class corpus_tester
{
public:
corpus_tester();
bool test(const char* pCmd_line);
private:
void print_comparative_metric_stats(const command_line_params& params, const crnlib::vector<image_utils::error_metrics>& stats1, const crnlib::vector<image_utils::error_metrics>& stats2, uint num_blocks_x, uint num_blocks_y);
void print_metric_stats(const crnlib::vector<image_utils::error_metrics>& stats, uint num_blocks_x, uint num_blocks_y);
image_u8 m_bad_block_img;
uint m_next_bad_block_index;
uint m_total_bad_block_files;
void flush_bad_blocks();
void add_bad_block(image_u8& block);
};
} // namespace crnlib
+16
View File
@@ -350,6 +350,22 @@
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
> >
<File
RelativePath=".\corpus_gen.cpp"
>
</File>
<File
RelativePath=".\corpus_gen.h"
>
</File>
<File
RelativePath=".\corpus_test.cpp"
>
</File>
<File
RelativePath=".\corpus_test.h"
>
</File>
<File <File
RelativePath=".\crunch.cpp" RelativePath=".\crunch.cpp"
> >
+4
View File
@@ -43,6 +43,10 @@
<Add directory="..\inc" /> <Add directory="..\inc" />
<Add directory="..\crnlib" /> <Add directory="..\crnlib" />
</Compiler> </Compiler>
<Unit filename="corpus_gen.cpp" />
<Unit filename="corpus_gen.h" />
<Unit filename="corpus_test.cpp" />
<Unit filename="corpus_test.h" />
<Unit filename="crunch.cpp" /> <Unit filename="crunch.cpp" />
<Extensions> <Extensions>
<code_completion /> <code_completion />
+76 -39
View File
@@ -5,6 +5,8 @@
// lifting is actually done by functions in the crnlib::texture_conversion namespace, // lifting is actually done by functions in the crnlib::texture_conversion namespace,
// which are mostly wrappers over the public crnlib.h functions. // which are mostly wrappers over the public crnlib.h functions.
// See Copyright Notice and license at the end of inc/crnlib.h // See Copyright Notice and license at the end of inc/crnlib.h
//
// Important: If compiling with gcc, be sure strict aliasing is disabled: -fno-strict-aliasing
#include "crn_core.h" #include "crn_core.h"
#include "crn_console.h" #include "crn_console.h"
@@ -21,6 +23,9 @@
#define CRND_HEADER_FILE_ONLY #define CRND_HEADER_FILE_ONLY
#include "crn_decomp.h" #include "crn_decomp.h"
#include "corpus_gen.h"
#include "corpus_test.h"
using namespace crnlib; using namespace crnlib;
const int cDefaultCRNQualityLevel = 128; const int cDefaultCRNQualityLevel = 128;
@@ -69,9 +74,10 @@ public:
console::printf("crunch [options] -file filename"); console::printf("crunch [options] -file filename");
console::printf("-file filename - Required input filename, wildcards, multiple -file params OK."); console::printf("-file filename - Required input filename, wildcards, multiple -file params OK.");
console::printf("-file @list.txt - List of files to convert."); console::printf("-file @list.txt - List of files to convert.");
console::printf("Supported source file formats: dds,crn,tga,bmp,png,jpg/jpeg,psd"); console::printf("Supported source file formats: dds,ktx,crn,tga,bmp,png,jpg/jpeg,psd");
console::printf("Note: Some file format variants are unsupported, such as progressive JPEG's."); console::printf("Note: Some file format variants are unsupported.");
console::printf("See the docs for stb_image.c: http://www.nothings.org/stb_image.c"); console::printf("See the docs for stb_image.c: http://www.nothings.org/stb_image.c");
console::printf("Progressive JPEG files are supported, see: http://code.google.com/p/jpeg-compressor/");
console::message("\nPath/file related parameters:"); console::message("\nPath/file related parameters:");
console::printf("-out filename - Output filename"); console::printf("-out filename - Output filename");
@@ -82,7 +88,7 @@ public:
console::printf("-timestamp - Update only changed files"); console::printf("-timestamp - Update only changed files");
console::printf("-forcewrite - Overwrite read-only files"); console::printf("-forcewrite - Overwrite read-only files");
console::printf("-recreate - Recreate directory structure"); console::printf("-recreate - Recreate directory structure");
console::printf("-fileformat [dds,crn,tga,bmp] - Output file format, default=crn or dds"); console::printf("-fileformat [dds,ktx,crn,tga,bmp,png] - Output file format, default=crn or dds");
console::message("\nModes:"); console::message("\nModes:");
console::printf("-compare - Compare input and output files (no output files are written)."); console::printf("-compare - Compare input and output files (no output files are written).");
@@ -103,7 +109,9 @@ public:
console::printf("-imagestats - Print various image qualilty statistics"); console::printf("-imagestats - Print various image qualilty statistics");
console::printf("-mipstats - Print statistics for each mipmap, not just the top mip"); console::printf("-mipstats - Print statistics for each mipmap, not just the top mip");
console::printf("-lzmastats - Print size of output file compressed with LZMA codec"); console::printf("-lzmastats - Print size of output file compressed with LZMA codec");
console::printf("-split - Write faces/mip levels to multiple separate output files"); console::printf("-split - Write faces/mip levels to multiple separate output PNG files");
console::printf("-yflip - Always flip texture on Y axis before processing");
console::printf("-unflip - Unflip texture if read from source file as flipped");
console::message("\nImage rescaling (mutually exclusive options)"); console::message("\nImage rescaling (mutually exclusive options)");
console::printf("-rescale <int> <int> - Rescale image to specified resolution"); console::printf("-rescale <int> <int> - Rescale image to specified resolution");
@@ -251,6 +259,9 @@ public:
{ "lzmastats", 0, false }, { "lzmastats", 0, false },
{ "split", 0, false }, { "split", 0, false },
{ "csvfile", 1, false }, { "csvfile", 1, false },
{ "yflip", 0, false },
{ "unflip", 0, false },
}; };
crnlib::vector<command_line_params::param_desc> params; crnlib::vector<command_line_params::param_desc> params;
@@ -398,6 +409,11 @@ private:
{ {
dynamic_string find_name(input_spec); dynamic_string find_name(input_spec);
if ((find_name.get_len()) && (file_utils::does_dir_exist(find_name.get_ptr())))
{
file_utils::combine_path(find_name, find_name.get_ptr(), "*");
}
if ((find_name.is_empty()) || (!file_utils::full_path(find_name))) if ((find_name.is_empty()) || (!file_utils::full_path(find_name)))
{ {
console::error("Invalid input filename: %s", find_name.get_ptr()); console::error("Invalid input filename: %s", find_name.get_ptr());
@@ -421,6 +437,7 @@ private:
console::error("Failed finding files: %s", find_name.get_ptr()); console::error("Failed finding files: %s", find_name.get_ptr());
return false; return false;
} }
if (file_finder.get_files().empty()) if (file_finder.get_files().empty())
{ {
console::warning("No files found: %s", find_name.get_ptr()); console::warning("No files found: %s", find_name.get_ptr());
@@ -479,8 +496,12 @@ private:
out_file_type = texture_file_types::cFormatBMP; out_file_type = texture_file_types::cFormatBMP;
else if (fmt == "dds") else if (fmt == "dds")
out_file_type = texture_file_types::cFormatDDS; out_file_type = texture_file_types::cFormatDDS;
else if (fmt == "ktx")
out_file_type = texture_file_types::cFormatKTX;
else if (fmt == "crn") else if (fmt == "crn")
out_file_type = texture_file_types::cFormatCRN; out_file_type = texture_file_types::cFormatCRN;
else if (fmt == "png")
out_file_type = texture_file_types::cFormatPNG;
else else
{ {
console::error("Unsupported output file type: %s", fmt.get_ptr()); console::error("Unsupported output file type: %s", fmt.get_ptr());
@@ -488,19 +509,26 @@ private:
} }
} }
// No explicit output format has been specified - try to determine something doable.
if (!m_params.has_key("fileformat")) if (!m_params.has_key("fileformat"))
{ {
if (m_params.has_key("split")) if (m_params.has_key("split"))
{ {
out_file_type = texture_file_types::cFormatTGA; out_file_type = texture_file_types::cFormatPNG;
} }
else else
{ {
texture_file_types::format input_file_type = texture_file_types::determine_file_format(in_filename.get_ptr()); texture_file_types::format input_file_type = texture_file_types::determine_file_format(in_filename.get_ptr());
if (input_file_type == texture_file_types::cFormatCRN) if (input_file_type == texture_file_types::cFormatCRN)
{ {
// Automatically transcode CRN->DXTc and write to DDS files, unless the user specifies either the /fileformat or /split options.
out_file_type = texture_file_types::cFormatDDS; out_file_type = texture_file_types::cFormatDDS;
} }
else if (input_file_type == texture_file_types::cFormatKTX)
{
// Default to converting KTX files to PNG
out_file_type = texture_file_types::cFormatPNG;
}
} }
} }
@@ -528,15 +556,22 @@ private:
{ {
dynamic_string out_dir(m_params.get_value_as_string_or_empty("outdir")); dynamic_string out_dir(m_params.get_value_as_string_or_empty("outdir"));
if (m_params.get_value_as_bool("recreate")) if (m_params.get_value_as_bool("recreate") && file_desc.m_rel.get_len())
{ {
file_utils::combine_path(out_dir, out_dir.get_ptr(), file_desc.m_rel.get_ptr()); file_utils::combine_path(out_dir, out_dir.get_ptr(), file_desc.m_rel.get_ptr());
} }
if (out_dir.get_len()) if (out_dir.get_len())
out_filename.format("%s\\%s.%s", out_dir.get_ptr(), in_fname.get_ptr(), texture_file_types::get_extension(out_file_type)); {
if (file_utils::is_path_separator(out_dir.back()))
out_filename.format("%s%s.%s", out_dir.get_ptr(), in_fname.get_ptr(), texture_file_types::get_extension(out_file_type));
else
out_filename.format("%s\\%s.%s", out_dir.get_ptr(), in_fname.get_ptr(), texture_file_types::get_extension(out_file_type));
}
else else
{
out_filename.format("%s.%s", in_fname.get_ptr(), texture_file_types::get_extension(out_file_type)); out_filename.format("%s.%s", in_fname.get_ptr(), texture_file_types::get_extension(out_file_type));
}
if (m_params.get_value_as_bool("recreate")) if (m_params.get_value_as_bool("recreate"))
{ {
@@ -621,7 +656,7 @@ private:
return true; return true;
} }
void print_texture_info(const char* pTex_desc, texture_conversion::convert_params& params, dds_texture& tex) void print_texture_info(const char* pTex_desc, texture_conversion::convert_params& params, mipmapped_texture& tex)
{ {
console::info("%s: %ux%u, Levels: %u, Faces: %u, Format: %s", console::info("%s: %ux%u, Levels: %u, Faces: %u, Format: %s",
pTex_desc, pTex_desc,
@@ -642,6 +677,7 @@ private:
if (tex.get_comp_flags() & pixel_format_helpers::cCompFlagGrayscale) console::info("Grayscale "); if (tex.get_comp_flags() & pixel_format_helpers::cCompFlagGrayscale) console::info("Grayscale ");
if (tex.get_comp_flags() & pixel_format_helpers::cCompFlagNormalMap) console::info("NormalMap "); if (tex.get_comp_flags() & pixel_format_helpers::cCompFlagNormalMap) console::info("NormalMap ");
if (tex.get_comp_flags() & pixel_format_helpers::cCompFlagLumaChroma) console::info("LumaChroma "); if (tex.get_comp_flags() & pixel_format_helpers::cCompFlagLumaChroma) console::info("LumaChroma ");
if (tex.is_flipped()) console::info("Flipped "); else console::info("Non-Flipped ");
console::info("\n"); console::info("\n");
console::enable_crlf(); console::enable_crlf();
} }
@@ -817,7 +853,7 @@ private:
{ {
const char *pKeyName = m_params.has_key("q") ? "q" : "quality"; const char *pKeyName = m_params.has_key("q") ? "q" : "quality";
if ((dst_file_format == texture_file_types::cFormatDDS) || (dst_file_format == texture_file_types::cFormatCRN)) if ((dst_file_format == texture_file_types::cFormatDDS) || (dst_file_format == texture_file_types::cFormatCRN) || (dst_file_format == texture_file_types::cFormatKTX))
{ {
uint32 i = m_params.get_value_as_int(pKeyName, 0, cDefaultCRNQualityLevel, 0, cCRNMaxQualityLevel); uint32 i = m_params.get_value_as_int(pKeyName, 0, cDefaultCRNQualityLevel, 0, cCRNMaxQualityLevel);
@@ -825,7 +861,7 @@ private:
} }
else else
{ {
console::error("/quality or /q option is only invalid when writing DDS or CRN files!"); console::error("/quality or /q option is only invalid when writing DDS, KTX or CRN files!");
return false; return false;
} }
} }
@@ -937,8 +973,8 @@ private:
return cCSFailed; return cCSFailed;
} }
dds_texture src_tex; mipmapped_texture src_tex;
if (!src_tex.load_from_file(pSrc_filename, src_file_format)) if (!src_tex.read_from_file(pSrc_filename, src_file_format))
{ {
if (src_tex.get_last_error().is_empty()) if (src_tex.get_last_error().is_empty())
console::error("Failed reading source file: \"%s\"", pSrc_filename); console::error("Failed reading source file: \"%s\"", pSrc_filename);
@@ -982,12 +1018,14 @@ private:
compressed_size = cmp_tex_bytes.size(); compressed_size = cmp_tex_bytes.size();
} }
} }
console::info("Source texture dimensions: %ux%u, Levels: %u, Faces: %u, Format: %s", console::info("Source texture dimensions: %ux%u, Levels: %u, Faces: %u, Format: %s\nPacked Format: %u, Apparent Type: %s, Flipped: %u, Can Unflip Without Unpacking: %u",
src_tex.get_width(), src_tex.get_width(),
src_tex.get_height(), src_tex.get_height(),
src_tex.get_num_levels(), src_tex.get_num_levels(),
src_tex.get_num_faces(), src_tex.get_num_faces(),
pixel_format_helpers::get_pixel_format_string(src_tex.get_format())); pixel_format_helpers::get_pixel_format_string(src_tex.get_format()),
src_tex.is_packed(), get_texture_type_desc(src_tex.determine_texture_type()),
src_tex.is_flipped(), src_tex.can_unflip_without_unpacking());
console::info("Total pixels: %u, Source file size: " CRNLIB_UINT64_FORMAT_SPECIFIER ", Source file bits/pixel: %1.3f", console::info("Total pixels: %u, Source file size: " CRNLIB_UINT64_FORMAT_SPECIFIER ", Source file bits/pixel: %1.3f",
total_in_pixels, input_file_size, (input_file_size * 8.0f) / total_in_pixels); total_in_pixels, input_file_size, (input_file_size * 8.0f) / total_in_pixels);
@@ -1054,9 +1092,9 @@ private:
return cCSFailed; return cCSFailed;
} }
dds_texture src_tex; mipmapped_texture src_tex;
if (!src_tex.load_from_file(pSrc_filename, src_file_format)) if (!src_tex.read_from_file(pSrc_filename, src_file_format))
{ {
if (src_tex.get_last_error().is_empty()) if (src_tex.get_last_error().is_empty())
console::error("Failed reading source file: \"%s\"", pSrc_filename); console::error("Failed reading source file: \"%s\"", pSrc_filename);
@@ -1091,9 +1129,9 @@ private:
return cCSFailed; return cCSFailed;
} }
dds_texture src_tex; mipmapped_texture src_tex;
tim.start(); tim.start();
if (!src_tex.load_from_file(pSrc_filename, src_file_format)) if (!src_tex.read_from_file(pSrc_filename, src_file_format))
{ {
if (src_tex.get_last_error().is_empty()) if (src_tex.get_last_error().is_empty())
console::error("Failed reading source file: \"%s\"", pSrc_filename); console::error("Failed reading source file: \"%s\"", pSrc_filename);
@@ -1118,7 +1156,9 @@ private:
params.m_dst_file_type = out_file_type; params.m_dst_file_type = out_file_type;
params.m_lzma_stats = m_params.has_key("lzmastats"); params.m_lzma_stats = m_params.has_key("lzmastats");
params.m_write_mipmaps_to_multiple_files = m_params.has_key("split"); params.m_write_mipmaps_to_multiple_files = m_params.has_key("split");
params.m_use_source_format = m_params.has_key("usesourceformat"); params.m_always_use_source_pixel_format = m_params.has_key("usesourceformat");
params.m_y_flip = m_params.has_key("yflip");
params.m_unflip = m_params.has_key("unflip");
if ((!m_params.get_value_as_bool("noprogress")) && (!m_params.get_value_as_bool("quiet"))) if ((!m_params.get_value_as_bool("noprogress")) && (!m_params.get_value_as_bool("quiet")))
params.m_pProgress_func = progress_callback_func; params.m_pProgress_func = progress_callback_func;
@@ -1233,12 +1273,25 @@ static int main_internal(int argc, char *argv[])
print_title(); print_title();
crunch converter;
dynamic_string cmd_line; dynamic_string cmd_line;
get_command_line(cmd_line, argc, argv); get_command_line_as_single_string(cmd_line, argc, argv);
bool status = converter.convert(cmd_line.get_ptr()); bool status = false;
if (check_for_option(argc, argv, "corpus_gen"))
{
corpus_gen generator;
status = generator.generate(cmd_line.get_ptr());
}
else if (check_for_option(argc, argv, "corpus_test"))
{
corpus_tester tester;
status = tester.test(cmd_line.get_ptr());
}
else
{
crunch converter;
status = converter.convert(cmd_line.get_ptr());
}
colorized_console::deinit(); colorized_console::deinit();
@@ -1247,7 +1300,6 @@ static int main_internal(int argc, char *argv[])
return status ? EXIT_SUCCESS : EXIT_FAILURE; return status ? EXIT_SUCCESS : EXIT_FAILURE;
} }
static void pause_and_wait(void) static void pause_and_wait(void)
{ {
console::enable_output(); console::enable_output();
@@ -1265,21 +1317,6 @@ static void pause_and_wait(void)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
#if 0
dynamic_string path, filename;
file_utils::split_path("/usr/local/blah/xxx.tga", path, filename);
dynamic_string combined;
file_utils::combine_path(combined, path.get_ptr(), filename.get_ptr());
find_files ff;
bool b = ff.find("/home/richg", "*", find_files::cFlagAllowFiles|find_files::cFlagAllowDirs|find_files::cFlagRecursive);
for (uint32 i = 0; i < ff.get_files().size(); i++)
{
printf("%s dir:%i\n", ff.get_files()[i].m_fullname.get_ptr(), ff.get_files()[i].m_is_dir);
}
#endif
int status = EXIT_FAILURE; int status = EXIT_FAILURE;
if (crnlib_is_debugger_present()) if (crnlib_is_debugger_present())
+12 -4
View File
@@ -8,15 +8,15 @@
<Build> <Build>
<Target title="Debug"> <Target title="Debug">
<Option output="../bin_linux/crunchD" prefix_auto="1" extension_auto="1" /> <Option output="../bin_linux/crunchD" prefix_auto="1" extension_auto="1" />
<Option working_dir="/home/richg/crunch_work/bin_linux" />
<Option object_output="obj/Debug/" /> <Option object_output="obj/Debug/" />
<Option external_deps="../crnlib/libcrnlibD.a;" /> <Option external_deps="../crnlib/libcrnlibD.a;" />
<Option type="1" /> <Option type="1" />
<Option compiler="gcc" /> <Option compiler="gcc" />
<Option parameters='-deep &quot;/media/truecrypt1/x/*.jpg&quot; -bitrate 1.5' /> <Option parameters="-file orig/*.png -fileformat crn -bitrate 1.33 " />
<Compiler> <Compiler>
<Add option="-Wextra" /> <Add option="-Wextra" />
<Add option="-Wall" /> <Add option="-Wall" />
<Add option="-g" />
<Add option="-D_DEBUG" /> <Add option="-D_DEBUG" />
</Compiler> </Compiler>
<Linker> <Linker>
@@ -26,11 +26,12 @@
</Target> </Target>
<Target title="Release"> <Target title="Release">
<Option output="../bin_linux/crunch" prefix_auto="1" extension_auto="1" /> <Option output="../bin_linux/crunch" prefix_auto="1" extension_auto="1" />
<Option working_dir="/home/richg/crunch_work/bin_linux" />
<Option object_output="obj/Release/" /> <Option object_output="obj/Release/" />
<Option external_deps="../crnlib/libcrnlib.a;" /> <Option external_deps="../crnlib/libcrnlib.a;" />
<Option type="1" /> <Option type="1" />
<Option compiler="gcc" /> <Option compiler="gcc" />
<Option parameters='-deep &quot;/media/truecrypt1/x/*.jpg&quot; -bitrate 1.5' /> <Option parameters="-file orig/*.png -fileformat crn -bitrate 1.33 -imagestats -lzmastats -logfile log3.txt -fileformat crn -bitrate 1.33 -imagestats -lzmastats -logfile log3.txt -bitrate 1.5" />
<Compiler> <Compiler>
<Add option="-march=core2" /> <Add option="-march=core2" />
<Add option="-fomit-frame-pointer" /> <Add option="-fomit-frame-pointer" />
@@ -41,7 +42,6 @@
<Add option="-DNDEBUG" /> <Add option="-DNDEBUG" />
</Compiler> </Compiler>
<Linker> <Linker>
<Add option="-s" />
<Add library="../crnlib/libcrnlib.a" /> <Add library="../crnlib/libcrnlib.a" />
<Add library="pthread" /> <Add library="pthread" />
</Linker> </Linker>
@@ -50,12 +50,20 @@
<Compiler> <Compiler>
<Add option="-Wextra" /> <Add option="-Wextra" />
<Add option="-Wall" /> <Add option="-Wall" />
<Add option="-g" />
<Add option="-fexceptions" /> <Add option="-fexceptions" />
<Add option="-Wno-unused-value" /> <Add option="-Wno-unused-value" />
<Add option="-Wno-unused" /> <Add option="-Wno-unused" />
<Add option="-fno-strict-aliasing" />
<Add option="-ffast-math" />
<Add option="-fno-math-errno" />
<Add directory="../inc" /> <Add directory="../inc" />
<Add directory="../crnlib" /> <Add directory="../crnlib" />
</Compiler> </Compiler>
<Unit filename="corpus_gen.cpp" />
<Unit filename="corpus_gen.h" />
<Unit filename="corpus_test.cpp" />
<Unit filename="corpus_test.h" />
<Unit filename="crunch.cpp" /> <Unit filename="crunch.cpp" />
<Extensions> <Extensions>
<code_completion /> <code_completion />
+6 -3
View File
@@ -8,15 +8,16 @@
// Note: This is a single file, stand-alone C++ library which is controlled by the use of two macros: // Note: This is a single file, stand-alone C++ library which is controlled by the use of two macros:
// If CRND_INCLUDE_CRND_H is NOT defined, the header is included. // If CRND_INCLUDE_CRND_H is NOT defined, the header is included.
// If CRND_HEADER_FILE_ONLY is NOT defined, the implementation is included. // If CRND_HEADER_FILE_ONLY is NOT defined, the implementation is included.
//
// Important: If compiling with gcc, be sure strict aliasing is disabled: -fno-strict-aliasing
#ifndef CRND_INCLUDE_CRND_H #ifndef CRND_INCLUDE_CRND_H
#define CRND_INCLUDE_CRND_H #define CRND_INCLUDE_CRND_H
// Include crnlib.h (only to bring in some basic CRN-related types). // Include crnlib.h (only to bring in some basic CRN-related types).
#include "crnlib.h" #include "crnlib.h"
#define CRND_LIB_VERSION 103 #define CRND_LIB_VERSION 104
#define CRND_VERSION_STRING "01.03" #define CRND_VERSION_STRING "01.04"
#ifdef _DEBUG #ifdef _DEBUG
#define CRND_BUILD_DEBUG #define CRND_BUILD_DEBUG
@@ -2624,6 +2625,7 @@ namespace crnd
case cCRNFmtDXT5_xGxR: return CRND_FOURCC('x', 'G', 'x', 'R'); case cCRNFmtDXT5_xGxR: return CRND_FOURCC('x', 'G', 'x', 'R');
case cCRNFmtDXT5_xGBR: return CRND_FOURCC('x', 'G', 'B', 'R'); case cCRNFmtDXT5_xGBR: return CRND_FOURCC('x', 'G', 'B', 'R');
case cCRNFmtDXT5_AGBR: return CRND_FOURCC('A', 'G', 'B', 'R'); case cCRNFmtDXT5_AGBR: return CRND_FOURCC('A', 'G', 'B', 'R');
case cCRNFmtETC1: return CRND_FOURCC('E', 'T', 'C', '1');
default: break; default: break;
} }
CRND_ASSERT(false); CRND_ASSERT(false);
@@ -2650,6 +2652,7 @@ namespace crnd
{ {
case cCRNFmtDXT1: case cCRNFmtDXT1:
case cCRNFmtDXT5A: case cCRNFmtDXT5A:
case cCRNFmtETC1:
return 4; return 4;
case cCRNFmtDXT3: case cCRNFmtDXT3:
case cCRNFmtDXT5: case cCRNFmtDXT5:
+16 -3
View File
@@ -10,6 +10,7 @@
// The crn_decomp.h header file library contains all the code necessary for // The crn_decomp.h header file library contains all the code necessary for
// decompression. // decompression.
// //
// Important: If compiling with gcc, be sure strict aliasing is disabled: -fno-strict-aliasing
#ifndef CRNLIB_H #ifndef CRNLIB_H
#define CRNLIB_H #define CRNLIB_H
@@ -17,7 +18,7 @@
#pragma warning (disable: 4127) // conditional expression is constant #pragma warning (disable: 4127) // conditional expression is constant
#endif #endif
#define CRNLIB_VERSION 103 #define CRNLIB_VERSION 104
#define CRNLIB_SUPPORT_ATI_COMPRESS 0 #define CRNLIB_SUPPORT_ATI_COMPRESS 0
#define CRNLIB_SUPPORT_SQUISH 0 #define CRNLIB_SUPPORT_SQUISH 0
@@ -71,6 +72,8 @@ enum crn_format
// DXT5 alpha blocks only // DXT5 alpha blocks only
cCRNFmtDXT5A, cCRNFmtDXT5A,
cCRNFmtETC1,
cCRNFmtTotal, cCRNFmtTotal,
cCRNFmtForceDWORD = 0xFFFFFFFF cCRNFmtForceDWORD = 0xFFFFFFFF
@@ -168,13 +171,14 @@ enum crn_dxt_quality
// Which DXTn compressor to use when compressing to plain (non-clustered) .DDS. // Which DXTn compressor to use when compressing to plain (non-clustered) .DDS.
enum crn_dxt_compressor_type enum crn_dxt_compressor_type
{ {
cCRNDXTCompressorCRN, // Use crnlib's DXTc block compressor (default, highest quality, comparable or better than ati_compress or squish) cCRNDXTCompressorCRN, // Use crnlib's ETC1 or DXTc block compressor (default, highest quality, comparable or better than ati_compress or squish, and crnlib's ETC1 is a lot fasterw with similiar quality to Erricson's)
cCRNDXTCompressorCRNF, // Use crnlib's "fast" DXTc block compressor cCRNDXTCompressorCRNF, // Use crnlib's "fast" DXTc block compressor
cCRNDXTCompressorRYG, // Use RYG's DXTc block compressor (low quality, but very fast) cCRNDXTCompressorRYG, // Use RYG's DXTc block compressor (low quality, but very fast)
#if CRNLIB_SUPPORT_ATI_COMPRESS #if CRNLIB_SUPPORT_ATI_COMPRESS
cCRNDXTCompressorATI, cCRNDXTCompressorATI,
#endif #endif
#if CRNLIB_SUPPORT_SQUISH #if CRNLIB_SUPPORT_SQUISH
cCRNDXTCompressorSquish, cCRNDXTCompressorSquish,
#endif #endif
@@ -588,17 +592,26 @@ const char* crn_get_dxt_quality_string(crn_dxt_quality q);
typedef void *crn_block_compressor_context_t; typedef void *crn_block_compressor_context_t;
// Create a DXTn block compressor. // Create a DXTn block compressor.
// Notes this function only supports the basic/nonswizzled DXTn formats (DXT1, DXT3, DXT5, DXT5A, DXN_XY and DXN_YX). // This function only supports the basic/nonswizzled "fundamental" formats: DXT1, DXT3, DXT5, DXT5A, DXN_XY and DXN_YX.
// Avoid calling this multiple times if you intend on compressing many blocks, because it allocates some memory. // Avoid calling this multiple times if you intend on compressing many blocks, because it allocates some memory.
crn_block_compressor_context_t crn_create_block_compressor(const crn_comp_params &params); crn_block_compressor_context_t crn_create_block_compressor(const crn_comp_params &params);
// Compresses a block of 16 pixels to the destination DXTn block. // Compresses a block of 16 pixels to the destination DXTn block.
// pDst_block should be 8 (for DXT1/DXT5A) or 16 bytes (all the others). // pDst_block should be 8 (for DXT1/DXT5A) or 16 bytes (all the others).
// pPixels should be an array of 16 crn_uint32's. Each crn_uint32 must be r,g,b,a (r is always first) in memory.
void crn_compress_block(crn_block_compressor_context_t pContext, const crn_uint32 *pPixels, void *pDst_block); void crn_compress_block(crn_block_compressor_context_t pContext, const crn_uint32 *pPixels, void *pDst_block);
// Frees a DXTn block compressor. // Frees a DXTn block compressor.
void crn_free_block_compressor(crn_block_compressor_context_t pContext); void crn_free_block_compressor(crn_block_compressor_context_t pContext);
// Unpacks a compressed block to pDst_pixels.
// pSrc_block should be 8 (for DXT1/DXT5A) or 16 bytes (all the others).
// pDst_pixel should be an array of 16 crn_uint32's. Each uint32 will be r,g,b,a (r is always first) in memory.
// crn_fmt should be one of the "fundamental" formats: DXT1, DXT3, DXT5, DXT5A, DXN_XY and DXN_YX.
// The various swizzled DXT5 formats (such as cCRNFmtDXT5_xGBR, etc.) will be unpacked as if they where plain DXT5.
// Returns false if the crn_fmt is invalid.
bool crn_decompress_block(const void *pSrc_block, crn_uint32 *pDst_pixels, crn_format crn_fmt);
#endif // CRNLIB_H #endif // CRNLIB_H
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
+4 -3
View File
@@ -22,13 +22,14 @@ namespace crnlib
PIXEL_FMT_DXN = CRNLIB_PIXEL_FMT_FOURCC('A', '2', 'X', 'Y'), // DXN_XY PIXEL_FMT_DXN = CRNLIB_PIXEL_FMT_FOURCC('A', '2', 'X', 'Y'), // DXN_XY
PIXEL_FMT_DXT5A = CRNLIB_PIXEL_FMT_FOURCC('A', 'T', 'I', '1'), // ATI1N, http://developer.amd.com/media/gpu_assets/Radeon_X1x00_Programming_Guide.pdf PIXEL_FMT_DXT5A = CRNLIB_PIXEL_FMT_FOURCC('A', 'T', 'I', '1'), // ATI1N, http://developer.amd.com/media/gpu_assets/Radeon_X1x00_Programming_Guide.pdf
// Non-standard, crnlib-specific pixel formats (some of these are supported by ATI's compressonator) // Non-standard, crnlib-specific pixel formats (some of these are supported by ATI's Compressonator)
PIXEL_FMT_DXT5_CCxY = CRNLIB_PIXEL_FMT_FOURCC('C', 'C', 'x', 'Y'), PIXEL_FMT_DXT5_CCxY = CRNLIB_PIXEL_FMT_FOURCC('C', 'C', 'x', 'Y'),
PIXEL_FMT_DXT5_xGxR = CRNLIB_PIXEL_FMT_FOURCC('x', 'G', 'x', 'R'), PIXEL_FMT_DXT5_xGxR = CRNLIB_PIXEL_FMT_FOURCC('x', 'G', 'x', 'R'),
PIXEL_FMT_DXT5_xGBR = CRNLIB_PIXEL_FMT_FOURCC('x', 'G', 'B', 'R'), PIXEL_FMT_DXT5_xGBR = CRNLIB_PIXEL_FMT_FOURCC('x', 'G', 'B', 'R'),
PIXEL_FMT_DXT5_AGBR = CRNLIB_PIXEL_FMT_FOURCC('A', 'G', 'B', 'R'), PIXEL_FMT_DXT5_AGBR = CRNLIB_PIXEL_FMT_FOURCC('A', 'G', 'B', 'R'),
PIXEL_FMT_DXT1A = CRNLIB_PIXEL_FMT_FOURCC('D', 'X', '1', 'A'), PIXEL_FMT_DXT1A = CRNLIB_PIXEL_FMT_FOURCC('D', 'X', '1', 'A'),
PIXEL_FMT_ETC1 = CRNLIB_PIXEL_FMT_FOURCC('E', 'T', 'C', '1'),
PIXEL_FMT_R8G8B8 = CRNLIB_PIXEL_FMT_FOURCC('R', 'G', 'B', 'x'), PIXEL_FMT_R8G8B8 = CRNLIB_PIXEL_FMT_FOURCC('R', 'G', 'B', 'x'),
PIXEL_FMT_L8 = CRNLIB_PIXEL_FMT_FOURCC('L', 'x', 'x', 'x'), PIXEL_FMT_L8 = CRNLIB_PIXEL_FMT_FOURCC('L', 'x', 'x', 'x'),
@@ -56,7 +57,7 @@ namespace crnlib
crn_uint32 dwSize; crn_uint32 dwSize;
crn_uint32 dwFlags; crn_uint32 dwFlags;
crn_uint32 dwFourCC; crn_uint32 dwFourCC;
crn_uint32 dwRGBBitCount; // ATI compressonator and crnlib sometimes place a FOURCC code here crn_uint32 dwRGBBitCount; // ATI compressonator and crnlib will place a FOURCC code here for swizzled/cooked DXTn formats
crn_uint32 dwRBitMask; crn_uint32 dwRBitMask;
crn_uint32 dwGBitMask; crn_uint32 dwGBitMask;
crn_uint32 dwBBitMask; crn_uint32 dwBBitMask;
@@ -121,7 +122,7 @@ namespace crnlib
const crn_uint32 DDSD_FVF = 0x00200000; const crn_uint32 DDSD_FVF = 0x00200000;
const crn_uint32 DDSD_SRCVBHANDLE = 0x00400000; const crn_uint32 DDSD_SRCVBHANDLE = 0x00400000;
const crn_uint32 DDSD_DEPTH = 0x00800000; const crn_uint32 DDSD_DEPTH = 0x00800000;
const crn_uint32 DDSD_ALL = 0x00fff9ee; const crn_uint32 DDSD_ALL = 0x00fff9ee;
const crn_uint32 DDPF_ALPHAPIXELS = 0x00000001; const crn_uint32 DDPF_ALPHAPIXELS = 0x00000001;
+28 -3
View File
@@ -1,4 +1,4 @@
crunch/crnlib v1.03 PRERELEASE - Advanced DXTn texture compression library crunch/crnlib v1.04 - Advanced DXTn texture compression library
Copyright (C) 2010-2012 Rich Geldreich and Tenacious Software LLC Copyright (C) 2010-2012 Rich Geldreich and Tenacious Software LLC
For bugs or support contact Rich Geldreich <richgel99@gmail.com>. For bugs or support contact Rich Geldreich <richgel99@gmail.com>.
@@ -6,12 +6,21 @@ For bugs or support contact Rich Geldreich <richgel99@gmail.com>.
This software uses the ZLIB license, which is located in license.txt. This software uses the ZLIB license, which is located in license.txt.
http://opensource.org/licenses/Zlib http://opensource.org/licenses/Zlib
Non-critical portions of this software make use of public domain code Portions of this software make use of public domain code originally
originally written by Igor Pavlov (LZMA) and RYG (crn_ryg_dxt*). written by Igor Pavlov (LZMA), RYG (crn_ryg_dxt*), and Sean Barrett (stb_image.c).
If you use this software in a product, an acknowledgment in the product If you use this software in a product, an acknowledgment in the product
documentation would be highly appreciated but is not required. documentation would be highly appreciated but is not required.
New for v1.04 [11/24/12]: KTX file format, basic ETC1 support, DDS format fixes, simple makefile
------------------------------------------------
Lots of higher level changes to get crnlib into a state where I can carry it forward to
support other file and texture compression formats. No major codec-level changes.
I've regression tested the codec writing .CRN and RDO .DDS files at various bitrates.
Everything seems OK, but with all the changes I made to support KTX and other formats like ETC1
I'm still worried about bugs.
New for v1.03 [4/26/12]: crnlib more portable, Linux Port New for v1.03 [4/26/12]: crnlib more portable, Linux Port
------------------------------------------------ ------------------------------------------------
@@ -135,6 +144,18 @@ further lossless compression because they're already highly compressed.
.CRN files are a bit more difficult/risky to integrate into a project, but .CRN files are a bit more difficult/risky to integrate into a project, but
the resulting compression ratio and quality is superior vs. clustered .DDS files. the resulting compression ratio and quality is superior vs. clustered .DDS files.
.KTX
crnlib and crunch can read/write the .KTX file format in various pixel formats.
Rate distortion optimization (clustered DXTc compression) is not yet supported
when writing .KTX files.
The .KTX file format is just like .DDS, except it's a fairly well specified
standard created by the Khronos Group. Unfortunately, almost all of the tools I've
found that support .KTX are fairly (to very) buggy, or are limited to only a handful
of pixel formats, so there's no guarantee that the .KTX files written by crnlib can
be reliably read by other tools.
Building the Examples Building the Examples
--------------------- ---------------------
@@ -280,3 +301,7 @@ sucks verses DXT5 alpha blocks, so I don't see this as a bug deal.)
* The DXT5_CCXY format uses a simple YCoCg encoding that is workable but * The DXT5_CCXY format uses a simple YCoCg encoding that is workable but
hasn't been tuned for max. quality yet. hasn't been tuned for max. quality yet.
* Clustered (or rate distortion optimized) DXTc compression is only
supported when writing to .DDS, not .KTX. Also, only plain block by block
compression is supported when writing to ETC1, and .CRN does not support ETC1.