1684 lines
56 KiB
C++
1684 lines
56 KiB
C++
// File: crn_dxt_image.cpp
|
|
// See Copyright Notice and license at the end of inc/crnlib.h
|
|
#include "crn_core.h"
|
|
#include "crn_dxt_image.h"
|
|
#if CRNLIB_SUPPORT_SQUISH
|
|
#include "squish\squish.h"
|
|
#endif
|
|
#include "crn_ryg_dxt.hpp"
|
|
#include "crn_dxt_fast.h"
|
|
#include "crn_console.h"
|
|
#include "crn_threading.h"
|
|
|
|
#if CRNLIB_SUPPORT_ATI_COMPRESS
|
|
#ifdef _DLL
|
|
#pragma comment(lib, "ATI_Compress_MT_DLL_VC8.lib")
|
|
#else
|
|
#pragma comment(lib, "ATI_Compress_MT_VC8.lib")
|
|
#endif
|
|
#include "..\ext\ATI_Compress\ATI_Compress.h"
|
|
#endif
|
|
|
|
#include "crn_rg_etc1.h"
|
|
#include "crn_etc.h"
|
|
#define CRNLIB_USE_RG_ETC1 1
|
|
|
|
namespace crnlib
|
|
{
|
|
dxt_image::dxt_image() :
|
|
m_pElements(NULL),
|
|
m_width(0),
|
|
m_height(0),
|
|
m_blocks_x(0),
|
|
m_blocks_y(0),
|
|
m_total_blocks(0),
|
|
m_total_elements(0),
|
|
m_num_elements_per_block(0),
|
|
m_bytes_per_block(0),
|
|
m_format(cDXTInvalid)
|
|
{
|
|
utils::zero_object(m_element_type);
|
|
utils::zero_object(m_element_component_index);
|
|
}
|
|
|
|
dxt_image::dxt_image(const dxt_image& other) :
|
|
m_pElements(NULL)
|
|
{
|
|
*this = other;
|
|
}
|
|
|
|
dxt_image& dxt_image::operator= (const dxt_image& rhs)
|
|
{
|
|
if (this == &rhs)
|
|
return *this;
|
|
|
|
clear();
|
|
|
|
m_width = rhs.m_width;
|
|
m_height = rhs.m_height;
|
|
m_blocks_x = rhs.m_blocks_x;
|
|
m_blocks_y = rhs.m_blocks_y;
|
|
m_num_elements_per_block = rhs.m_num_elements_per_block;
|
|
m_bytes_per_block = rhs.m_bytes_per_block;
|
|
m_format = rhs.m_format;
|
|
m_total_blocks = rhs.m_total_blocks;
|
|
m_total_elements = rhs.m_total_elements;
|
|
m_pElements = NULL;
|
|
memcpy(m_element_type, rhs.m_element_type, sizeof(m_element_type));
|
|
memcpy(m_element_component_index, rhs.m_element_component_index, sizeof(m_element_component_index));
|
|
|
|
if (rhs.m_pElements)
|
|
{
|
|
m_elements.resize(m_total_elements);
|
|
memcpy(&m_elements[0], rhs.m_pElements, sizeof(element) * m_total_elements);
|
|
m_pElements = &m_elements[0];
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
void dxt_image::clear()
|
|
{
|
|
m_elements.clear();
|
|
m_width = 0;
|
|
m_height = 0;
|
|
m_blocks_x = 0;
|
|
m_blocks_y = 0;
|
|
m_num_elements_per_block = 0;
|
|
m_bytes_per_block = 0;
|
|
m_format = cDXTInvalid;
|
|
utils::zero_object(m_element_type);
|
|
utils::zero_object(m_element_component_index);
|
|
m_total_blocks = 0;
|
|
m_total_elements = 0;
|
|
m_pElements = NULL;
|
|
}
|
|
|
|
bool dxt_image::init_internal(dxt_format fmt, uint width, uint height)
|
|
{
|
|
CRNLIB_ASSERT((fmt != cDXTInvalid) && (width > 0) && (height > 0));
|
|
|
|
clear();
|
|
|
|
m_width = width;
|
|
m_height = height;
|
|
|
|
m_blocks_x = (m_width + 3) >> cDXTBlockShift;
|
|
m_blocks_y = (m_height + 3) >> cDXTBlockShift;
|
|
|
|
m_num_elements_per_block = 2;
|
|
if ((fmt == cDXT1) || (fmt == cDXT1A) || (fmt == cDXT5A) || (fmt == cETC1))
|
|
m_num_elements_per_block = 1;
|
|
|
|
m_total_blocks = m_blocks_x * m_blocks_y;
|
|
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_format = fmt;
|
|
|
|
switch (m_format)
|
|
{
|
|
case cDXT1:
|
|
case cDXT1A:
|
|
{
|
|
m_element_type[0] = cColorDXT1;
|
|
m_element_component_index[0] = -1;
|
|
break;
|
|
}
|
|
case cDXT3:
|
|
{
|
|
m_element_type[0] = cAlphaDXT3;
|
|
m_element_type[1] = cColorDXT1;
|
|
m_element_component_index[0] = 3;
|
|
m_element_component_index[1] = -1;
|
|
break;
|
|
}
|
|
case cDXT5:
|
|
{
|
|
m_element_type[0] = cAlphaDXT5;
|
|
m_element_type[1] = cColorDXT1;
|
|
m_element_component_index[0] = 3;
|
|
m_element_component_index[1] = -1;
|
|
break;
|
|
}
|
|
case cDXT5A:
|
|
{
|
|
m_element_type[0] = cAlphaDXT5;
|
|
m_element_component_index[0] = 3;
|
|
break;
|
|
}
|
|
case cDXN_XY:
|
|
{
|
|
m_element_type[0] = cAlphaDXT5;
|
|
m_element_type[1] = cAlphaDXT5;
|
|
m_element_component_index[0] = 0;
|
|
m_element_component_index[1] = 1;
|
|
break;
|
|
}
|
|
case cDXN_YX:
|
|
{
|
|
m_element_type[0] = cAlphaDXT5;
|
|
m_element_type[1] = cAlphaDXT5;
|
|
m_element_component_index[0] = 1;
|
|
m_element_component_index[1] = 0;
|
|
break;
|
|
}
|
|
case cETC1:
|
|
{
|
|
m_element_type[0] = cColorETC1;
|
|
m_element_component_index[0] = -1;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
CRNLIB_ASSERT(0);
|
|
clear();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool dxt_image::init(dxt_format fmt, uint width, uint height, bool clear_elements)
|
|
{
|
|
if (!init_internal(fmt, width, height))
|
|
return false;
|
|
|
|
m_elements.resize(m_total_elements);
|
|
m_pElements = &m_elements[0];
|
|
|
|
if (clear_elements)
|
|
memset(m_pElements, 0, sizeof(element) * m_total_elements);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool dxt_image::init(dxt_format fmt, uint width, uint height, uint num_elements, element* pElements, bool create_copy)
|
|
{
|
|
CRNLIB_ASSERT(num_elements && pElements);
|
|
|
|
if (!init_internal(fmt, width, height))
|
|
return false;
|
|
|
|
if (num_elements != m_total_elements)
|
|
{
|
|
clear();
|
|
return false;
|
|
}
|
|
|
|
if (create_copy)
|
|
{
|
|
m_elements.resize(m_total_elements);
|
|
m_pElements = &m_elements[0];
|
|
|
|
memcpy(m_pElements, pElements, m_total_elements * sizeof(element));
|
|
}
|
|
else
|
|
m_pElements = pElements;
|
|
|
|
return true;
|
|
}
|
|
|
|
struct init_task_params
|
|
{
|
|
dxt_format m_fmt;
|
|
const image_u8* m_pImg;
|
|
const dxt_image::pack_params* m_pParams;
|
|
crn_thread_id_t m_main_thread;
|
|
atomic32_t m_canceled;
|
|
};
|
|
|
|
void dxt_image::init_task(uint64 data, void* pData_ptr)
|
|
{
|
|
const uint thread_index = static_cast<uint>(data);
|
|
init_task_params* pInit_params = static_cast<init_task_params*>(pData_ptr);
|
|
|
|
const image_u8& img = *pInit_params->m_pImg;
|
|
const pack_params& p = *pInit_params->m_pParams;
|
|
const bool is_main_thread = (crn_get_current_thread_id() == pInit_params->m_main_thread);
|
|
|
|
uint block_index = 0;
|
|
|
|
set_block_pixels_context optimizer_context;
|
|
int prev_progress_percentage = -1;
|
|
|
|
for (uint block_y = 0; block_y < m_blocks_y; block_y++)
|
|
{
|
|
const uint pixel_ofs_y = block_y * cDXTBlockSize;
|
|
|
|
for (uint block_x = 0; block_x < m_blocks_x; block_x++, block_index++)
|
|
{
|
|
if (pInit_params->m_canceled)
|
|
return;
|
|
|
|
if (p.m_pProgress_callback && is_main_thread && ((block_index & 63) == 63))
|
|
{
|
|
const uint progress_percentage = p.m_progress_start + ((block_index * p.m_progress_range + get_total_blocks() / 2) / get_total_blocks());
|
|
if ((int)progress_percentage != prev_progress_percentage)
|
|
{
|
|
prev_progress_percentage = progress_percentage;
|
|
if (!(p.m_pProgress_callback)(progress_percentage, p.m_pProgress_callback_user_data_ptr))
|
|
{
|
|
atomic_exchange32(&pInit_params->m_canceled, CRNLIB_TRUE);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (p.m_num_helper_threads)
|
|
{
|
|
if ((block_index % (p.m_num_helper_threads + 1)) != thread_index)
|
|
continue;
|
|
}
|
|
|
|
color_quad_u8 pixels[cDXTBlockSize * cDXTBlockSize];
|
|
|
|
const uint pixel_ofs_x = block_x * cDXTBlockSize;
|
|
|
|
for (uint y = 0; y < cDXTBlockSize; y++)
|
|
{
|
|
const uint iy = math::minimum(pixel_ofs_y + y, img.get_height() - 1);
|
|
|
|
for (uint x = 0; x < cDXTBlockSize; x++)
|
|
{
|
|
const uint ix = math::minimum(pixel_ofs_x + x, img.get_width() - 1);
|
|
|
|
pixels[x + y * cDXTBlockSize] = img(ix, iy);
|
|
}
|
|
}
|
|
|
|
set_block_pixels(block_x, block_y, pixels, p, optimizer_context);
|
|
}
|
|
}
|
|
}
|
|
|
|
#if CRNLIB_SUPPORT_ATI_COMPRESS
|
|
bool dxt_image::init_ati_compress(dxt_format fmt, const image_u8& img, const pack_params& p)
|
|
{
|
|
image_u8 tmp_img(img);
|
|
for (uint y = 0; y < img.get_height(); y++)
|
|
{
|
|
for (uint x = 0; x < img.get_width(); x++)
|
|
{
|
|
color_quad_u8 c(img(x, y));
|
|
std::swap(c.r, c.b);
|
|
tmp_img(x, y) = c;
|
|
}
|
|
}
|
|
|
|
ATI_TC_Texture src_tex;
|
|
utils::zero_object(src_tex);
|
|
src_tex.dwSize = sizeof(ATI_TC_Texture);
|
|
src_tex.dwWidth = tmp_img.get_width();
|
|
src_tex.dwHeight = tmp_img.get_height();
|
|
src_tex.dwPitch = tmp_img.get_pitch_in_bytes();
|
|
src_tex.format = ATI_TC_FORMAT_ARGB_8888;
|
|
src_tex.dwDataSize = src_tex.dwPitch * tmp_img.get_height();
|
|
src_tex.pData = (ATI_TC_BYTE*)tmp_img.get_ptr();
|
|
|
|
ATI_TC_Texture dst_tex;
|
|
utils::zero_object(dst_tex);
|
|
dst_tex.dwSize = sizeof(ATI_TC_Texture);
|
|
dst_tex.dwWidth = tmp_img.get_width();
|
|
dst_tex.dwHeight = tmp_img.get_height();
|
|
dst_tex.dwDataSize = get_size_in_bytes();
|
|
dst_tex.pData = (ATI_TC_BYTE*)get_element_ptr();
|
|
|
|
switch (fmt)
|
|
{
|
|
case cDXT1:
|
|
case cDXT1A:
|
|
dst_tex.format = ATI_TC_FORMAT_DXT1;
|
|
break;
|
|
case cDXT3:
|
|
dst_tex.format = ATI_TC_FORMAT_DXT3;
|
|
break;
|
|
case cDXT5:
|
|
dst_tex.format = ATI_TC_FORMAT_DXT5;
|
|
break;
|
|
case cDXT5A:
|
|
dst_tex.format = ATI_TC_FORMAT_ATI1N;
|
|
break;
|
|
case cDXN_XY:
|
|
dst_tex.format = ATI_TC_FORMAT_ATI2N_XY;
|
|
break;
|
|
case cDXN_YX:
|
|
dst_tex.format = ATI_TC_FORMAT_ATI2N;
|
|
break;
|
|
default:
|
|
{
|
|
CRNLIB_ASSERT(false);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
ATI_TC_CompressOptions options;
|
|
utils::zero_object(options);
|
|
options.dwSize = sizeof(ATI_TC_CompressOptions);
|
|
|
|
if (fmt == cDXT1A)
|
|
{
|
|
options.bDXT1UseAlpha = true;
|
|
options.nAlphaThreshold = (ATI_TC_BYTE)p.m_dxt1a_alpha_threshold;
|
|
}
|
|
options.bDisableMultiThreading = (p.m_num_helper_threads == 0);
|
|
switch (p.m_quality)
|
|
{
|
|
case cCRNDXTQualityFast:
|
|
options.nCompressionSpeed = ATI_TC_Speed_Fast;
|
|
break;
|
|
case cCRNDXTQualitySuperFast:
|
|
options.nCompressionSpeed = ATI_TC_Speed_SuperFast;
|
|
break;
|
|
default:
|
|
options.nCompressionSpeed = ATI_TC_Speed_Normal;
|
|
break;
|
|
}
|
|
|
|
if (p.m_perceptual)
|
|
{
|
|
options.bUseChannelWeighting = true;
|
|
options.fWeightingRed = .212671f;
|
|
options.fWeightingGreen = .715160f;
|
|
options.fWeightingBlue = .072169f;
|
|
}
|
|
|
|
ATI_TC_ERROR err = ATI_TC_ConvertTexture(&src_tex, &dst_tex, &options, NULL, NULL, NULL);
|
|
return err == ATI_TC_OK;
|
|
}
|
|
#endif
|
|
|
|
bool dxt_image::init(dxt_format fmt, const image_u8& img, const pack_params& p)
|
|
{
|
|
if (!init(fmt, img.get_width(), img.get_height(), false))
|
|
return false;
|
|
|
|
#if CRNLIB_SUPPORT_ATI_COMPRESS
|
|
if (p.m_compressor == cCRNDXTCompressorATI)
|
|
return init_ati_compress(fmt, img, p);
|
|
#endif
|
|
|
|
task_pool *pPool = p.m_pTask_pool;
|
|
|
|
task_pool tmp_pool;
|
|
if (!pPool)
|
|
{
|
|
if (!tmp_pool.init(p.m_num_helper_threads))
|
|
return false;
|
|
pPool = &tmp_pool;
|
|
}
|
|
|
|
init_task_params init_params;
|
|
init_params.m_fmt = fmt;
|
|
init_params.m_pImg = &img;
|
|
init_params.m_pParams = &p;
|
|
init_params.m_main_thread = crn_get_current_thread_id();
|
|
init_params.m_canceled = false;
|
|
|
|
for (uint i = 0; i <= p.m_num_helper_threads; i++)
|
|
pPool->queue_object_task(this, &dxt_image::init_task, i, &init_params);
|
|
|
|
pPool->join();
|
|
|
|
if (init_params.m_canceled)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool dxt_image::unpack(image_u8& img) const
|
|
{
|
|
if (!m_total_elements)
|
|
return false;
|
|
|
|
img.resize(m_width, m_height);
|
|
|
|
color_quad_u8 pixels[cDXTBlockSize * cDXTBlockSize];
|
|
for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
|
|
pixels[i].set(0, 0, 0, 255);
|
|
|
|
bool all_blocks_valid = true;
|
|
for (uint block_y = 0; block_y < m_blocks_y; block_y++)
|
|
{
|
|
const uint pixel_ofs_y = block_y * cDXTBlockSize;
|
|
const uint limit_y = math::minimum<uint>(cDXTBlockSize, img.get_height() - pixel_ofs_y);
|
|
|
|
for (uint block_x = 0; block_x < m_blocks_x; block_x++)
|
|
{
|
|
if (!get_block_pixels(block_x, block_y, pixels))
|
|
all_blocks_valid = false;
|
|
|
|
const uint pixel_ofs_x = block_x * cDXTBlockSize;
|
|
|
|
const uint limit_x = math::minimum<uint>(cDXTBlockSize, img.get_width() - pixel_ofs_x);
|
|
|
|
for (uint y = 0; y < limit_y; y++)
|
|
{
|
|
const uint iy = pixel_ofs_y + y;
|
|
|
|
for (uint x = 0; x < limit_x; x++)
|
|
{
|
|
const uint ix = pixel_ofs_x + x;
|
|
|
|
img(ix, iy) = pixels[x + (y << cDXTBlockShift)];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!all_blocks_valid)
|
|
console::error("dxt_image::unpack: One or more invalid blocks encountered!");
|
|
|
|
img.reset_comp_flags();
|
|
img.set_component_valid(0, false);
|
|
img.set_component_valid(1, false);
|
|
img.set_component_valid(2, false);
|
|
for (uint i = 0; i < m_num_elements_per_block; i++)
|
|
{
|
|
if (m_element_component_index[i] < 0)
|
|
{
|
|
img.set_component_valid(0, true);
|
|
img.set_component_valid(1, true);
|
|
img.set_component_valid(2, true);
|
|
}
|
|
else
|
|
img.set_component_valid(m_element_component_index[i], true);
|
|
}
|
|
|
|
img.set_component_valid(3, get_dxt_format_has_alpha(m_format));
|
|
|
|
return true;
|
|
}
|
|
|
|
void dxt_image::endian_swap()
|
|
{
|
|
utils::endian_switch_words(reinterpret_cast<uint16*>(m_elements.get_ptr()), m_elements.size_in_bytes() / sizeof(uint16));
|
|
}
|
|
|
|
const dxt_image::element& dxt_image::get_element(uint block_x, uint block_y, uint element_index) const
|
|
{
|
|
CRNLIB_ASSERT((block_x < m_blocks_x) && (block_y < m_blocks_y) && (element_index < m_num_elements_per_block));
|
|
return m_pElements[(block_x + block_y * m_blocks_x) * m_num_elements_per_block + element_index];
|
|
}
|
|
|
|
dxt_image::element& dxt_image::get_element(uint block_x, uint block_y, uint element_index)
|
|
{
|
|
CRNLIB_ASSERT((block_x < m_blocks_x) && (block_y < m_blocks_y) && (element_index < m_num_elements_per_block));
|
|
return m_pElements[(block_x + block_y * m_blocks_x) * m_num_elements_per_block + element_index];
|
|
}
|
|
|
|
bool dxt_image::has_alpha() const
|
|
{
|
|
switch (m_format)
|
|
{
|
|
case cDXT1:
|
|
{
|
|
for (uint i = 0; i < m_total_elements; i++)
|
|
{
|
|
const dxt1_block& blk = *(dxt1_block*)&m_pElements[i];
|
|
|
|
if (blk.get_low_color() <= blk.get_high_color())
|
|
{
|
|
for (uint y = 0; y < cDXTBlockSize; y++)
|
|
for (uint x = 0; x < cDXTBlockSize; x++)
|
|
if (blk.get_selector(x, y) == 3)
|
|
return true;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case cDXT1A:
|
|
case cDXT3:
|
|
case cDXT5:
|
|
case cDXT5A:
|
|
return true;
|
|
default: break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
color_quad_u8 dxt_image::get_pixel(uint x, uint y) const
|
|
{
|
|
CRNLIB_ASSERT((x < m_width) && (y < m_height));
|
|
|
|
const uint block_x = x >> cDXTBlockShift;
|
|
const uint block_y = y >> cDXTBlockShift;
|
|
|
|
const element* pElement = reinterpret_cast<const element*>(&get_element(block_x, block_y, 0));
|
|
|
|
color_quad_u8 result(0, 0, 0, 255);
|
|
|
|
for (uint element_index = 0; element_index < m_num_elements_per_block; element_index++, pElement++)
|
|
{
|
|
switch (m_element_type[element_index])
|
|
{
|
|
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 uint l = pBlock->get_low_color();
|
|
const uint h = pBlock->get_high_color();
|
|
|
|
color_quad_u8 c0(dxt1_block::unpack_color(static_cast<uint16>(l), true));
|
|
color_quad_u8 c1(dxt1_block::unpack_color(static_cast<uint16>(h), true));
|
|
|
|
const uint s = pBlock->get_selector(x & 3, y & 3);
|
|
|
|
if (l > h)
|
|
{
|
|
switch (s)
|
|
{
|
|
case 0: result.set_noclamp_rgb(c0.r, c0.g, c0.b); break;
|
|
case 1: result.set_noclamp_rgb(c1.r, c1.g, c1.b); break;
|
|
case 2: result.set_noclamp_rgb( (c0.r * 2 + c1.r) / 3, (c0.g * 2 + c1.g) / 3, (c0.b * 2 + c1.b) / 3); break;
|
|
case 3: result.set_noclamp_rgb( (c1.r * 2 + c0.r) / 3, (c1.g * 2 + c0.g) / 3, (c1.b * 2 + c0.b) / 3); break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (s)
|
|
{
|
|
case 0: result.set_noclamp_rgb(c0.r, c0.g, c0.b); break;
|
|
case 1: result.set_noclamp_rgb(c1.r, c1.g, c1.b); break;
|
|
case 2: result.set_noclamp_rgb( (c0.r + c1.r) >> 1U, (c0.g + c1.g) >> 1U, (c0.b + c1.b) >> 1U); break;
|
|
case 3:
|
|
{
|
|
if (m_format <= cDXT1A)
|
|
result.set_noclamp_rgba(0, 0, 0, 0);
|
|
else
|
|
result.set_noclamp_rgb(0, 0, 0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case cAlphaDXT5:
|
|
{
|
|
const int comp_index = m_element_component_index[element_index];
|
|
|
|
const dxt5_block* pBlock = reinterpret_cast<const dxt5_block*>(&get_element(block_x, block_y, element_index));
|
|
|
|
const uint l = pBlock->get_low_alpha();
|
|
const uint h = pBlock->get_high_alpha();
|
|
|
|
const uint s = pBlock->get_selector(x & 3, y & 3);
|
|
|
|
if (l > h)
|
|
{
|
|
switch (s)
|
|
{
|
|
case 0: result[comp_index] = static_cast<uint8>(l); break;
|
|
case 1: result[comp_index] = static_cast<uint8>(h); break;
|
|
case 2: result[comp_index] = static_cast<uint8>((l * 6 + h ) / 7); break;
|
|
case 3: result[comp_index] = static_cast<uint8>((l * 5 + h * 2) / 7); break;
|
|
case 4: result[comp_index] = static_cast<uint8>((l * 4 + h * 3) / 7); break;
|
|
case 5: result[comp_index] = static_cast<uint8>((l * 3 + h * 4) / 7); break;
|
|
case 6: result[comp_index] = static_cast<uint8>((l * 2 + h * 5) / 7); break;
|
|
case 7: result[comp_index] = static_cast<uint8>((l + h * 6) / 7); break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (s)
|
|
{
|
|
case 0: result[comp_index] = static_cast<uint8>(l); break;
|
|
case 1: result[comp_index] = static_cast<uint8>(h); break;
|
|
case 2: result[comp_index] = static_cast<uint8>((l * 4 + h ) / 5); break;
|
|
case 3: result[comp_index] = static_cast<uint8>((l * 3 + h * 2) / 5); break;
|
|
case 4: result[comp_index] = static_cast<uint8>((l * 2 + h * 3) / 5); break;
|
|
case 5: result[comp_index] = static_cast<uint8>((l + h * 4) / 5); break;
|
|
case 6: result[comp_index] = 0; break;
|
|
case 7: result[comp_index] = 255; break;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case cAlphaDXT3:
|
|
{
|
|
const int comp_index = m_element_component_index[element_index];
|
|
|
|
const dxt3_block* pBlock = reinterpret_cast<const dxt3_block*>(&get_element(block_x, block_y, element_index));
|
|
|
|
result[comp_index] = static_cast<uint8>(pBlock->get_alpha(x & 3, y & 3, true));
|
|
|
|
break;
|
|
}
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
uint dxt_image::get_pixel_alpha(uint x, uint y, uint element_index) const
|
|
{
|
|
CRNLIB_ASSERT((x < m_width) && (y < m_height) && (element_index < m_num_elements_per_block));
|
|
|
|
const uint block_x = x >> cDXTBlockShift;
|
|
const uint block_y = y >> cDXTBlockShift;
|
|
|
|
switch (m_element_type[element_index])
|
|
{
|
|
case cColorDXT1:
|
|
{
|
|
if (m_format <= cDXT1A)
|
|
{
|
|
const dxt1_block* pBlock = reinterpret_cast<const dxt1_block*>(&get_element(block_x, block_y, element_index));
|
|
|
|
const uint l = pBlock->get_low_color();
|
|
const uint h = pBlock->get_high_color();
|
|
|
|
if (l <= h)
|
|
{
|
|
uint s = pBlock->get_selector(x & 3, y & 3);
|
|
|
|
return (s == 3) ? 0 : 255;
|
|
}
|
|
else
|
|
{
|
|
return 255;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case cAlphaDXT5:
|
|
{
|
|
const dxt5_block* pBlock = reinterpret_cast<const dxt5_block*>(&get_element(block_x, block_y, element_index));
|
|
|
|
const uint l = pBlock->get_low_alpha();
|
|
const uint h = pBlock->get_high_alpha();
|
|
|
|
const uint s = pBlock->get_selector(x & 3, y & 3);
|
|
|
|
if (l > h)
|
|
{
|
|
switch (s)
|
|
{
|
|
case 0: return l;
|
|
case 1: return h;
|
|
case 2: return (l * 6 + h ) / 7;
|
|
case 3: return (l * 5 + h * 2) / 7;
|
|
case 4: return (l * 4 + h * 3) / 7;
|
|
case 5: return (l * 3 + h * 4) / 7;
|
|
case 6: return (l * 2 + h * 5) / 7;
|
|
case 7: return (l + h * 6) / 7;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (s)
|
|
{
|
|
case 0: return l;
|
|
case 1: return h;
|
|
case 2: return (l * 4 + h ) / 5;
|
|
case 3: return (l * 3 + h * 2) / 5;
|
|
case 4: return (l * 2 + h * 3) / 5;
|
|
case 5: return (l + h * 4) / 5;
|
|
case 6: return 0;
|
|
case 7: return 255;
|
|
}
|
|
}
|
|
}
|
|
case cAlphaDXT3:
|
|
{
|
|
const dxt3_block* pBlock = reinterpret_cast<const dxt3_block*>(&get_element(block_x, block_y, element_index));
|
|
|
|
return pBlock->get_alpha(x & 3, y & 3, true);
|
|
}
|
|
default: break;
|
|
}
|
|
|
|
return 255;
|
|
}
|
|
|
|
void dxt_image::set_pixel(uint x, uint y, const color_quad_u8& c, bool perceptual)
|
|
{
|
|
CRNLIB_ASSERT((x < m_width) && (y < m_height));
|
|
|
|
const uint block_x = x >> cDXTBlockShift;
|
|
const uint block_y = y >> cDXTBlockShift;
|
|
|
|
element* pElement = &get_element(block_x, block_y, 0);
|
|
|
|
for (uint element_index = 0; element_index < m_num_elements_per_block; element_index++, pElement++)
|
|
{
|
|
switch (m_element_type[element_index])
|
|
{
|
|
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);
|
|
|
|
color_quad_u8 colors[cDXT1SelectorValues];
|
|
const uint n = pDXT1_block->get_block_colors(colors, static_cast<uint16>(pDXT1_block->get_low_color()), static_cast<uint16>(pDXT1_block->get_high_color()));
|
|
|
|
if ((m_format == cDXT1A) && (c.a < 128))
|
|
pDXT1_block->set_selector(x & 3, y & 3, 3);
|
|
else
|
|
{
|
|
uint best_error = UINT_MAX;
|
|
uint best_selector = 0;
|
|
|
|
for (uint i = 0; i < n; i++)
|
|
{
|
|
uint error = color::color_distance(perceptual, colors[i], c, false);
|
|
if (error < best_error)
|
|
{
|
|
best_error = error;
|
|
best_selector = i;
|
|
}
|
|
}
|
|
|
|
pDXT1_block->set_selector(x & 3, y & 3, best_selector);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case cAlphaDXT5:
|
|
{
|
|
dxt5_block* pDXT5_block = reinterpret_cast<dxt5_block*>(pElement);
|
|
|
|
uint values[cDXT5SelectorValues];
|
|
dxt5_block::get_block_values(values, pDXT5_block->get_low_alpha(), pDXT5_block->get_high_alpha());
|
|
|
|
const int comp_index = m_element_component_index[element_index];
|
|
|
|
uint best_error = UINT_MAX;
|
|
uint best_selector = 0;
|
|
|
|
for (uint i = 0; i < cDXT5SelectorValues; i++)
|
|
{
|
|
uint error = labs(values[i] - c[comp_index]); // no need to square
|
|
|
|
if (error < best_error)
|
|
{
|
|
best_error = error;
|
|
best_selector = i;
|
|
}
|
|
}
|
|
|
|
pDXT5_block->set_selector(x & 3, y & 3, best_selector);
|
|
|
|
break;
|
|
}
|
|
case cAlphaDXT3:
|
|
{
|
|
const int comp_index = m_element_component_index[element_index];
|
|
|
|
dxt3_block* pDXT3_block = reinterpret_cast<dxt3_block*>(pElement);
|
|
|
|
pDXT3_block->set_alpha(x & 3, y & 3, c[comp_index], true);
|
|
|
|
break;
|
|
}
|
|
default: break;
|
|
}
|
|
} // element_index
|
|
}
|
|
|
|
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);
|
|
|
|
for (uint element_index = 0; element_index < m_num_elements_per_block; element_index++, pElement++)
|
|
{
|
|
switch (m_element_type[element_index])
|
|
{
|
|
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);
|
|
|
|
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++)
|
|
{
|
|
uint s = pDXT1_block->get_selector(i & 3, i >> 2);
|
|
|
|
pPixels[i].r = colors[s].r;
|
|
pPixels[i].g = colors[s].g;
|
|
pPixels[i].b = colors[s].b;
|
|
|
|
if (m_format <= cDXT1A)
|
|
pPixels[i].a = colors[s].a;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case cAlphaDXT5:
|
|
{
|
|
const dxt5_block* pDXT5_block = reinterpret_cast<const dxt5_block*>(pElement);
|
|
|
|
uint values[cDXT5SelectorValues];
|
|
dxt5_block::get_block_values(values, pDXT5_block->get_low_alpha(), pDXT5_block->get_high_alpha());
|
|
|
|
const int comp_index = m_element_component_index[element_index];
|
|
|
|
for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
|
|
{
|
|
uint s = pDXT5_block->get_selector(i & 3, i >> 2);
|
|
|
|
pPixels[i][comp_index] = static_cast<uint8>(values[s]);
|
|
}
|
|
|
|
break;
|
|
}
|
|
case cAlphaDXT3:
|
|
{
|
|
const dxt3_block* pDXT3_block = reinterpret_cast<const dxt3_block*>(pElement);
|
|
|
|
const int comp_index = m_element_component_index[element_index];
|
|
|
|
for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
|
|
{
|
|
uint a = pDXT3_block->get_alpha(i & 3, i >> 2, true);
|
|
|
|
pPixels[i][comp_index] = static_cast<uint8>(a);
|
|
}
|
|
|
|
break;
|
|
}
|
|
default: break;
|
|
}
|
|
} // 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)
|
|
{
|
|
set_block_pixels_context context;
|
|
set_block_pixels(block_x, block_y, pPixels, p, context);
|
|
}
|
|
|
|
void dxt_image::set_block_pixels(
|
|
uint block_x, uint block_y, const color_quad_u8* pPixels, const pack_params& p,
|
|
set_block_pixels_context& context)
|
|
{
|
|
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 ((p.m_compressor == cCRNDXTCompressorSquish) && ((m_format == cDXT1) || (m_format == cDXT1A) || (m_format == cDXT3) || (m_format == cDXT5) || (m_format == cDXT5A)))
|
|
{
|
|
uint squish_flags = 0;
|
|
if ((m_format == cDXT1) || (m_format == cDXT1A))
|
|
squish_flags = squish::kDxt1;
|
|
else if (m_format == cDXT3)
|
|
squish_flags = squish::kDxt3;
|
|
else if (m_format == cDXT5A)
|
|
squish_flags = squish::kDxt5A;
|
|
else
|
|
squish_flags = squish::kDxt5;
|
|
|
|
if (p.m_perceptual)
|
|
squish_flags |= squish::kColourMetricPerceptual;
|
|
else
|
|
squish_flags |= squish::kColourMetricUniform;
|
|
|
|
if (p.m_quality >= cCRNDXTQualityBetter)
|
|
squish_flags |= squish::kColourIterativeClusterFit;
|
|
else if (p.m_quality == cCRNDXTQualitySuperFast)
|
|
squish_flags |= squish::kColourRangeFit;
|
|
|
|
color_quad_u8 pixels[cDXTBlockSize * cDXTBlockSize];
|
|
|
|
memcpy(pixels, pPixels, sizeof(color_quad_u8) * cDXTBlockSize * cDXTBlockSize);
|
|
|
|
if (m_format == cDXT1)
|
|
{
|
|
for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
|
|
pixels[i].a = 255;
|
|
}
|
|
else if (m_format == cDXT1A)
|
|
{
|
|
for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
|
|
if (pixels[i].a < p.m_dxt1a_alpha_threshold)
|
|
pixels[i].a = 0;
|
|
else
|
|
pixels[i].a = 255;
|
|
}
|
|
|
|
squish::Compress(reinterpret_cast<const squish::u8*>(pixels), pElement, squish_flags);
|
|
}
|
|
|
|
else
|
|
#endif // CRNLIB_SUPPORT_SQUISH
|
|
// RYG doesn't support DXT1A
|
|
if ((p.m_compressor == cCRNDXTCompressorRYG) && ((m_format == cDXT1) || (m_format == cDXT5) || (m_format == cDXT5A)))
|
|
{
|
|
color_quad_u8 pixels[cDXTBlockSize * cDXTBlockSize];
|
|
|
|
for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
|
|
{
|
|
pixels[i].r = pPixels[i].b;
|
|
pixels[i].g = pPixels[i].g;
|
|
pixels[i].b = pPixels[i].r;
|
|
|
|
if (m_format == cDXT1)
|
|
pixels[i].a = 255;
|
|
else
|
|
pixels[i].a = pPixels[i].a;
|
|
}
|
|
|
|
if (m_format == cDXT5A)
|
|
ryg_dxt::sCompressDXT5ABlock((sU8*)pElement, (const sU32*)pixels, 0);
|
|
else
|
|
ryg_dxt::sCompressDXTBlock((sU8*)pElement, (const sU32*)pixels, m_format == cDXT5, 0);
|
|
}
|
|
else if ((p.m_compressor == cCRNDXTCompressorCRNF) && (m_format != cDXT1A))
|
|
{
|
|
for (uint element_index = 0; element_index < m_num_elements_per_block; element_index++, pElement++)
|
|
{
|
|
switch (m_element_type[element_index])
|
|
{
|
|
case cColorDXT1:
|
|
{
|
|
dxt1_block* pDXT1_block = reinterpret_cast<dxt1_block*>(pElement);
|
|
dxt_fast::compress_color_block(pDXT1_block, pPixels, p.m_quality >= cCRNDXTQualityNormal);
|
|
|
|
break;
|
|
}
|
|
case cAlphaDXT5:
|
|
{
|
|
dxt5_block* pDXT5_block = reinterpret_cast<dxt5_block*>(pElement);
|
|
dxt_fast::compress_alpha_block(pDXT5_block, pPixels, m_element_component_index[element_index]);
|
|
|
|
break;
|
|
}
|
|
case cAlphaDXT3:
|
|
{
|
|
const int comp_index = m_element_component_index[element_index];
|
|
|
|
dxt3_block* pDXT3_block = reinterpret_cast<dxt3_block*>(pElement);
|
|
|
|
for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
|
|
pDXT3_block->set_alpha(i & 3, i >> 2, pPixels[i][comp_index], true);
|
|
|
|
break;
|
|
}
|
|
default: break;
|
|
}
|
|
}
|
|
}
|
|
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++)
|
|
{
|
|
switch (m_element_type[element_index])
|
|
{
|
|
case cColorDXT1:
|
|
{
|
|
dxt1_block* pDXT1_block = reinterpret_cast<dxt1_block*>(pElement);
|
|
|
|
bool pixels_have_alpha = false;
|
|
if (m_format == cDXT1A)
|
|
{
|
|
for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
|
|
if (pPixels[i].a < p.m_dxt1a_alpha_threshold)
|
|
{
|
|
pixels_have_alpha = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
dxt1_endpoint_optimizer::results results;
|
|
uint8 selectors[cDXTBlockSize * cDXTBlockSize];
|
|
results.m_pSelectors = selectors;
|
|
|
|
dxt1_endpoint_optimizer::params params;
|
|
params.m_block_index = block_x + block_y * m_blocks_x;
|
|
params.m_quality = p.m_quality;
|
|
params.m_perceptual = p.m_perceptual;
|
|
params.m_grayscale_sampling = p.m_grayscale_sampling;
|
|
params.m_pixels_have_alpha = pixels_have_alpha;
|
|
params.m_use_alpha_blocks = p.m_use_both_block_types;
|
|
params.m_use_transparent_indices_for_black = p.m_use_transparent_indices_for_black;
|
|
params.m_dxt1a_alpha_threshold = p.m_dxt1a_alpha_threshold;
|
|
params.m_pPixels = pPixels;
|
|
params.m_num_pixels = cDXTBlockSize * cDXTBlockSize;
|
|
params.m_endpoint_caching = p.m_endpoint_caching;
|
|
params.m_color_weights[0] = p.m_color_weights[0];
|
|
params.m_color_weights[1] = p.m_color_weights[1];
|
|
params.m_color_weights[2] = p.m_color_weights[2];
|
|
|
|
if ((m_format != cDXT1) && (m_format != cDXT1A))
|
|
params.m_use_alpha_blocks = false;
|
|
|
|
if (!dxt1_optimizer.compute(params, results))
|
|
{
|
|
CRNLIB_ASSERT(0);
|
|
break;
|
|
}
|
|
|
|
pDXT1_block->set_low_color(results.m_low_color);
|
|
pDXT1_block->set_high_color(results.m_high_color);
|
|
|
|
for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
|
|
pDXT1_block->set_selector(i & 3, i >> 2, selectors[i]);
|
|
|
|
break;
|
|
}
|
|
case cAlphaDXT5:
|
|
{
|
|
dxt5_block* pDXT5_block = reinterpret_cast<dxt5_block*>(pElement);
|
|
|
|
dxt5_endpoint_optimizer::results results;
|
|
|
|
uint8 selectors[cDXTBlockSize * cDXTBlockSize];
|
|
results.m_pSelectors = selectors;
|
|
|
|
dxt5_endpoint_optimizer::params params;
|
|
params.m_block_index = block_x + block_y * m_blocks_x;
|
|
params.m_pPixels = pPixels;
|
|
params.m_num_pixels = cDXTBlockSize * cDXTBlockSize;
|
|
params.m_comp_index = m_element_component_index[element_index];
|
|
params.m_quality = p.m_quality;
|
|
params.m_use_both_block_types = p.m_use_both_block_types;
|
|
|
|
if (!dxt5_optimizer.compute(params, results))
|
|
{
|
|
CRNLIB_ASSERT(0);
|
|
break;
|
|
}
|
|
|
|
pDXT5_block->set_low_alpha(results.m_first_endpoint);
|
|
pDXT5_block->set_high_alpha(results.m_second_endpoint);
|
|
|
|
for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
|
|
pDXT5_block->set_selector(i & 3, i >> 2, selectors[i]);
|
|
|
|
break;
|
|
}
|
|
case cAlphaDXT3:
|
|
{
|
|
const int comp_index = m_element_component_index[element_index];
|
|
|
|
dxt3_block* pDXT3_block = reinterpret_cast<dxt3_block*>(pElement);
|
|
|
|
for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
|
|
pDXT3_block->set_alpha(i & 3, i >> 2, pPixels[i][comp_index], true);
|
|
|
|
break;
|
|
}
|
|
default: break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void dxt_image::get_block_endpoints(uint block_x, uint block_y, uint element_index, uint& packed_low_endpoint, uint& packed_high_endpoint) const
|
|
{
|
|
const element& block = get_element(block_x, block_y, element_index);
|
|
|
|
switch (m_element_type[element_index])
|
|
{
|
|
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);
|
|
|
|
packed_low_endpoint = block1.get_low_color();
|
|
packed_high_endpoint = block1.get_high_color();
|
|
|
|
break;
|
|
}
|
|
case cAlphaDXT5:
|
|
{
|
|
const dxt5_block& block5 = *reinterpret_cast<const dxt5_block*>(&block);
|
|
|
|
packed_low_endpoint = block5.get_low_alpha();
|
|
packed_high_endpoint = block5.get_high_alpha();
|
|
|
|
break;
|
|
}
|
|
case cAlphaDXT3:
|
|
{
|
|
packed_low_endpoint = 0;
|
|
packed_high_endpoint = 255;
|
|
|
|
break;
|
|
}
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
int dxt_image::get_block_endpoints(uint block_x, uint block_y, uint element_index, color_quad_u8& low_endpoint, color_quad_u8& high_endpoint, bool scaled) const
|
|
{
|
|
uint l = 0, h = 0;
|
|
get_block_endpoints(block_x, block_y, element_index, l, h);
|
|
|
|
switch (m_element_type[element_index])
|
|
{
|
|
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;
|
|
|
|
dxt1_block::unpack_color(r, g, b, static_cast<uint16>(l), scaled);
|
|
low_endpoint.r = static_cast<uint8>(r);
|
|
low_endpoint.g = static_cast<uint8>(g);
|
|
low_endpoint.b = static_cast<uint8>(b);
|
|
|
|
dxt1_block::unpack_color(r, g, b, static_cast<uint16>(h), scaled);
|
|
high_endpoint.r = static_cast<uint8>(r);
|
|
high_endpoint.g = static_cast<uint8>(g);
|
|
high_endpoint.b = static_cast<uint8>(b);
|
|
|
|
return -1;
|
|
}
|
|
case cAlphaDXT5:
|
|
{
|
|
const int component = m_element_component_index[element_index];
|
|
|
|
low_endpoint[component] = static_cast<uint8>(l);
|
|
high_endpoint[component] = static_cast<uint8>(h);
|
|
|
|
return component;
|
|
}
|
|
case cAlphaDXT3:
|
|
{
|
|
const int component = m_element_component_index[element_index];
|
|
|
|
low_endpoint[component] = static_cast<uint8>(l);
|
|
high_endpoint[component] = static_cast<uint8>(h);
|
|
|
|
return component;
|
|
}
|
|
default: break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
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);
|
|
|
|
switch (m_element_type[element_index])
|
|
{
|
|
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);
|
|
return dxt1_block::get_block_colors(pColors, static_cast<uint16>(block1.get_low_color()), static_cast<uint16>(block1.get_high_color()));
|
|
}
|
|
case cAlphaDXT5:
|
|
{
|
|
const dxt5_block& block5 = *reinterpret_cast<const dxt5_block*>(&block);
|
|
|
|
uint values[cDXT5SelectorValues];
|
|
|
|
const uint n = dxt5_block::get_block_values(values, block5.get_low_alpha(), block5.get_high_alpha());
|
|
|
|
const int comp_index = m_element_component_index[element_index];
|
|
for (uint i = 0; i < n; i++)
|
|
pColors[i][comp_index] = static_cast<uint8>(values[i]);
|
|
|
|
return n;
|
|
}
|
|
case cAlphaDXT3:
|
|
{
|
|
const int comp_index = m_element_component_index[element_index];
|
|
for (uint i = 0; i < 16; i++)
|
|
pColors[i][comp_index] = static_cast<uint8>((i << 4) | i);
|
|
|
|
return 16;
|
|
}
|
|
default: break;
|
|
}
|
|
|
|
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
|
|
{
|
|
CRNLIB_ASSERT((x < m_width) && (y < m_height));
|
|
|
|
const uint block_x = x >> cDXTBlockShift;
|
|
const uint block_y = y >> cDXTBlockShift;
|
|
|
|
const element& block = get_element(block_x, block_y, element_index);
|
|
|
|
switch (m_element_type[element_index])
|
|
{
|
|
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);
|
|
return block1.get_selector(x & 3, y & 3);
|
|
}
|
|
case cAlphaDXT5:
|
|
{
|
|
const dxt5_block& block5 = *reinterpret_cast<const dxt5_block*>(&block);
|
|
return block5.get_selector(x & 3, y & 3);
|
|
}
|
|
case cAlphaDXT3:
|
|
{
|
|
const dxt3_block& block3 = *reinterpret_cast<const dxt3_block*>(&block);
|
|
return block3.get_alpha(x & 3, y & 3, false);
|
|
}
|
|
default: break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void dxt_image::change_dxt1_to_dxt1a()
|
|
{
|
|
if (m_format == cDXT1)
|
|
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
|
|
|
|
|
|
|
|
|