362 lines
11 KiB
C
362 lines
11 KiB
C
|
// File: crn_dxt.h
|
||
|
// See Copyright Notice and license at the end of inc/crnlib.h
|
||
|
#pragma once
|
||
|
#include "../inc/crnlib.h"
|
||
|
#include "crn_color.h"
|
||
|
#include "crn_vec.h"
|
||
|
#include "crn_rand.h"
|
||
|
#include "crn_sparse_bit_array.h"
|
||
|
#include "crn_hash_map.h"
|
||
|
#include <map>
|
||
|
|
||
|
#define CRNLIB_DXT_ALT_ROUNDING 1
|
||
|
|
||
|
namespace crnlib
|
||
|
{
|
||
|
enum dxt_constants
|
||
|
{
|
||
|
cDXT1BytesPerBlock = 8U,
|
||
|
cDXT5NBytesPerBlock = 16U,
|
||
|
|
||
|
cDXT5SelectorBits = 3U,
|
||
|
cDXT5SelectorValues = 1U << cDXT5SelectorBits,
|
||
|
cDXT5SelectorMask = cDXT5SelectorValues - 1U,
|
||
|
|
||
|
cDXT1SelectorBits = 2U,
|
||
|
cDXT1SelectorValues = 1U << cDXT1SelectorBits,
|
||
|
cDXT1SelectorMask = cDXT1SelectorValues - 1U,
|
||
|
|
||
|
cDXTBlockShift = 2U,
|
||
|
cDXTBlockSize = 1U << cDXTBlockShift
|
||
|
};
|
||
|
|
||
|
enum dxt_format
|
||
|
{
|
||
|
cDXTInvalid = -1,
|
||
|
|
||
|
// cDXT1/1A must appear first!
|
||
|
cDXT1,
|
||
|
cDXT1A,
|
||
|
|
||
|
cDXT3,
|
||
|
cDXT5,
|
||
|
cDXT5A,
|
||
|
|
||
|
cDXN_XY, // inverted relative to standard ATI2, 360's DXN
|
||
|
cDXN_YX, // standard ATI2,
|
||
|
|
||
|
cETC1 // Ericsson texture compression (color only, 4x4 blocks, 4bpp, 64-bits/block)
|
||
|
};
|
||
|
|
||
|
const float cDXT1MaxLinearValue = 3.0f;
|
||
|
const float cDXT1InvMaxLinearValue = 1.0f/3.0f;
|
||
|
|
||
|
const float cDXT5MaxLinearValue = 7.0f;
|
||
|
const float cDXT5InvMaxLinearValue = 1.0f/7.0f;
|
||
|
|
||
|
// Converts DXT1 raw color selector index to a linear value.
|
||
|
extern const uint8 g_dxt1_to_linear[cDXT1SelectorValues];
|
||
|
|
||
|
// Converts DXT5 raw alpha selector index to a linear value.
|
||
|
extern const uint8 g_dxt5_to_linear[cDXT5SelectorValues];
|
||
|
|
||
|
// Converts DXT1 linear color selector index to a raw value (inverse of g_dxt1_to_linear).
|
||
|
extern const uint8 g_dxt1_from_linear[cDXT1SelectorValues];
|
||
|
|
||
|
// Converts DXT5 linear alpha selector index to a raw value (inverse of g_dxt5_to_linear).
|
||
|
extern const uint8 g_dxt5_from_linear[cDXT5SelectorValues];
|
||
|
|
||
|
extern const uint8 g_dxt5_alpha6_to_linear[cDXT5SelectorValues];
|
||
|
|
||
|
extern const uint8 g_six_alpha_invert_table[cDXT5SelectorValues];
|
||
|
extern const uint8 g_eight_alpha_invert_table[cDXT5SelectorValues];
|
||
|
|
||
|
const char* get_dxt_format_string(dxt_format fmt);
|
||
|
uint get_dxt_format_bits_per_pixel(dxt_format fmt);
|
||
|
bool get_dxt_format_has_alpha(dxt_format fmt);
|
||
|
|
||
|
const char* get_dxt_quality_string(crn_dxt_quality q);
|
||
|
|
||
|
const char* get_dxt_compressor_name(crn_dxt_compressor_type c);
|
||
|
|
||
|
struct dxt1_block
|
||
|
{
|
||
|
uint8 m_low_color[2];
|
||
|
uint8 m_high_color[2];
|
||
|
|
||
|
enum { cNumSelectorBytes = 4 };
|
||
|
uint8 m_selectors[cNumSelectorBytes];
|
||
|
|
||
|
inline void clear()
|
||
|
{
|
||
|
utils::zero_this(this);
|
||
|
}
|
||
|
|
||
|
// These methods assume the in-memory rep is in LE byte order.
|
||
|
inline uint get_low_color() const
|
||
|
{
|
||
|
return m_low_color[0] | (m_low_color[1] << 8U);
|
||
|
}
|
||
|
|
||
|
inline uint get_high_color() const
|
||
|
{
|
||
|
return m_high_color[0] | (m_high_color[1] << 8U);
|
||
|
}
|
||
|
|
||
|
inline void set_low_color(uint16 c)
|
||
|
{
|
||
|
m_low_color[0] = static_cast<uint8>(c & 0xFF);
|
||
|
m_low_color[1] = static_cast<uint8>((c >> 8) & 0xFF);
|
||
|
}
|
||
|
|
||
|
inline void set_high_color(uint16 c)
|
||
|
{
|
||
|
m_high_color[0] = static_cast<uint8>(c & 0xFF);
|
||
|
m_high_color[1] = static_cast<uint8>((c >> 8) & 0xFF);
|
||
|
}
|
||
|
|
||
|
inline bool is_constant_color_block() const { return get_low_color() == get_high_color(); }
|
||
|
inline bool is_alpha_block() const { return get_low_color() <= get_high_color(); }
|
||
|
inline bool is_non_alpha_block() const { return !is_alpha_block(); }
|
||
|
|
||
|
inline uint get_selector(uint x, uint y) const
|
||
|
{
|
||
|
CRNLIB_ASSERT((x < 4U) && (y < 4U));
|
||
|
return (m_selectors[y] >> (x * cDXT1SelectorBits)) & cDXT1SelectorMask;
|
||
|
}
|
||
|
|
||
|
inline void set_selector(uint x, uint y, uint val)
|
||
|
{
|
||
|
CRNLIB_ASSERT((x < 4U) && (y < 4U) && (val < 4U));
|
||
|
|
||
|
m_selectors[y] &= (~(cDXT1SelectorMask << (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(uint r, uint g, uint b, bool scaled, uint bias = 127U);
|
||
|
|
||
|
static color_quad_u8 unpack_color(uint16 packed_color, bool scaled, uint alpha = 255U);
|
||
|
static void unpack_color(uint& r, uint& g, uint& b, uint16 packed_color, bool scaled);
|
||
|
|
||
|
static uint get_block_colors3(color_quad_u8* pDst, uint16 color0, uint16 color1);
|
||
|
static uint get_block_colors3_round(color_quad_u8* pDst, uint16 color0, uint16 color1);
|
||
|
|
||
|
static uint get_block_colors4(color_quad_u8* pDst, uint16 color0, uint16 color1);
|
||
|
static uint get_block_colors4_round(color_quad_u8* pDst, uint16 color0, uint16 color1);
|
||
|
|
||
|
// pDst must point to an array at least cDXT1SelectorValues long.
|
||
|
static uint get_block_colors(color_quad_u8* pDst, uint16 color0, uint16 color1);
|
||
|
|
||
|
static uint get_block_colors_round(color_quad_u8* pDst, uint16 color0, uint16 color1);
|
||
|
|
||
|
static color_quad_u8 unpack_endpoint(uint32 endpoints, uint index, bool scaled, uint alpha = 255U);
|
||
|
static uint pack_endpoints(uint lo, uint hi);
|
||
|
|
||
|
static void get_block_colors_NV5x(color_quad_u8* pDst, uint16 packed_col0, uint16 packed_col1, bool color4);
|
||
|
};
|
||
|
|
||
|
CRNLIB_DEFINE_BITWISE_COPYABLE(dxt1_block);
|
||
|
|
||
|
struct dxt3_block
|
||
|
{
|
||
|
enum { cNumAlphaBytes = 8 };
|
||
|
uint8 m_alpha[cNumAlphaBytes];
|
||
|
|
||
|
void set_alpha(uint x, uint y, uint value, bool scaled);
|
||
|
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);
|
||
|
|
||
|
struct dxt5_block
|
||
|
{
|
||
|
uint8 m_endpoints[2];
|
||
|
|
||
|
enum { cNumSelectorBytes = 6 };
|
||
|
uint8 m_selectors[cNumSelectorBytes];
|
||
|
|
||
|
inline void clear()
|
||
|
{
|
||
|
utils::zero_this(this);
|
||
|
}
|
||
|
|
||
|
inline uint get_low_alpha() const
|
||
|
{
|
||
|
return m_endpoints[0];
|
||
|
}
|
||
|
|
||
|
inline uint get_high_alpha() const
|
||
|
{
|
||
|
return m_endpoints[1];
|
||
|
}
|
||
|
|
||
|
inline void set_low_alpha(uint i)
|
||
|
{
|
||
|
CRNLIB_ASSERT(i <= cUINT8_MAX);
|
||
|
m_endpoints[0] = static_cast<uint8>(i);
|
||
|
}
|
||
|
|
||
|
inline void set_high_alpha(uint i)
|
||
|
{
|
||
|
CRNLIB_ASSERT(i <= cUINT8_MAX);
|
||
|
m_endpoints[1] = static_cast<uint8>(i);
|
||
|
}
|
||
|
|
||
|
inline bool is_alpha6_block() const { return get_low_alpha() <= get_high_alpha(); }
|
||
|
|
||
|
uint get_endpoints_as_word() const { return m_endpoints[0] | (m_endpoints[1] << 8); }
|
||
|
uint get_selectors_as_word(uint index) { CRNLIB_ASSERT(index < 3); return m_selectors[index * 2] | (m_selectors[index * 2 + 1] << 8); }
|
||
|
|
||
|
inline uint get_selector(uint x, uint y) const
|
||
|
{
|
||
|
CRNLIB_ASSERT((x < 4U) && (y < 4U));
|
||
|
|
||
|
uint selector_index = (y * 4) + x;
|
||
|
uint bit_index = selector_index * cDXT5SelectorBits;
|
||
|
|
||
|
uint byte_index = bit_index >> 3;
|
||
|
uint bit_ofs = bit_index & 7;
|
||
|
|
||
|
uint v = m_selectors[byte_index];
|
||
|
if (byte_index < (cNumSelectorBytes - 1))
|
||
|
v |= (m_selectors[byte_index + 1] << 8);
|
||
|
|
||
|
return (v >> bit_ofs) & 7;
|
||
|
}
|
||
|
|
||
|
inline void set_selector(uint x, uint y, uint val)
|
||
|
{
|
||
|
CRNLIB_ASSERT((x < 4U) && (y < 4U) && (val < 8U));
|
||
|
|
||
|
uint selector_index = (y * 4) + x;
|
||
|
uint bit_index = selector_index * cDXT5SelectorBits;
|
||
|
|
||
|
uint byte_index = bit_index >> 3;
|
||
|
uint bit_ofs = bit_index & 7;
|
||
|
|
||
|
uint v = m_selectors[byte_index];
|
||
|
if (byte_index < (cNumSelectorBytes - 1))
|
||
|
v |= (m_selectors[byte_index + 1] << 8);
|
||
|
|
||
|
v &= (~(7 << bit_ofs));
|
||
|
v |= (val << bit_ofs);
|
||
|
|
||
|
m_selectors[byte_index] = static_cast<uint8>(v);
|
||
|
if (byte_index < (cNumSelectorBytes - 1))
|
||
|
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 };
|
||
|
|
||
|
// Results written to alpha channel.
|
||
|
static uint get_block_values6(color_quad_u8* pDst, uint l, uint h);
|
||
|
static uint get_block_values8(color_quad_u8* pDst, uint l, uint h);
|
||
|
static uint get_block_values(color_quad_u8* pDst, uint l, uint h);
|
||
|
|
||
|
static uint get_block_values6(uint* pDst, uint l, uint h);
|
||
|
static uint get_block_values8(uint* pDst, uint l, uint h);
|
||
|
// pDst must point to an array at least cDXT5SelectorValues long.
|
||
|
static uint get_block_values(uint* pDst, uint l, uint h);
|
||
|
|
||
|
static uint unpack_endpoint(uint packed, uint index);
|
||
|
static uint pack_endpoints(uint lo, uint hi);
|
||
|
};
|
||
|
|
||
|
CRNLIB_DEFINE_BITWISE_COPYABLE(dxt5_block);
|
||
|
|
||
|
struct dxt_pixel_block
|
||
|
{
|
||
|
color_quad_u8 m_pixels[cDXTBlockSize][cDXTBlockSize]; // [y][x]
|
||
|
|
||
|
inline void clear()
|
||
|
{
|
||
|
utils::zero_object(*this);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
CRNLIB_DEFINE_BITWISE_COPYABLE(dxt_pixel_block);
|
||
|
|
||
|
} // namespace crnlib
|
||
|
|
||
|
|
||
|
|