myou-engine/libs/bc7enc/bc7enc.cpp

161 lines
4.4 KiB
C++

#define BC7ENC_VERSION "1.08"
#define COMPUTE_SSIM (0)
#if _OPENMP
#include <omp.h>
#endif
#include <cstdint>
#include "bc7enc_rdo/rdo_bc_encoder.h"
#include "bc7enc_rdo/utils.h"
// This is based on main() in test.cpp to make a C function
// Valid formats: 1 3 4 5 7
// BC1 RGB 4bpp fast, small
// BC3 RGBA 8bpp fast
// BC4 gray 4bpp best for grayscale
// BC5 X+Y 8bpp
// BC7 RGB(A) 8bpp slow, best quality
typedef struct {
void *data;
int32_t len;
int32_t width;
int32_t height;
int8_t format; // BCn format to use.
// TODO: encoding settings
} EncodeBcInput;
typedef struct {
void *data;
int32_t len;
int32_t row_len;
} EncodeBcOutput;
typedef enum {
NO_ERROR = 0,
ENCODER_INIT_ERROR = 1,
ENCODE_ERROR = 2,
INVALID_SIZE_ERROR = 3,
UNSUPPORTED_FORMAT_ERROR = 4,
INVALID_INPUT_LENGTH_ERROR = 5,
INVALID_OUTPUT_LENGTH_ERROR = 6,
} EncodeBcError;
extern "C" EncodeBcError encode_bc(EncodeBcInput &input, EncodeBcOutput &output, bool verbose)
{
bool quiet_mode = !verbose;
int max_threads = 1;
#if _OPENMP
max_threads = std::min(std::max(1, omp_get_max_threads()), 128);
#endif
uint32_t pixel_format_bpp = 8;
rdo_bc::rdo_bc_params rp;
rp.m_rdo_max_threads = max_threads;
rp.m_status_output = !quiet_mode;
switch(input.format){
case 1:
rp.m_dxgi_format = DXGI_FORMAT_BC1_UNORM;
pixel_format_bpp = 4;
break;
case 3:
rp.m_dxgi_format = DXGI_FORMAT_BC3_UNORM;
break;
case 4:
rp.m_dxgi_format = DXGI_FORMAT_BC4_UNORM;
pixel_format_bpp = 4;
break;
case 5:
rp.m_dxgi_format = DXGI_FORMAT_BC5_UNORM;
break;
case 7:
// it's already default
break;
default:
return UNSUPPORTED_FORMAT_ERROR;
}
int32_t width = input.width;
int32_t height = input.height;
if(width == 0 || height == 0){
return INVALID_SIZE_ERROR;
}
utils::image_u8 source_image;
// TODO: avoid a copy somehow
source_image.init(width, height);
int32_t input_data_size = width * height * sizeof(uint32_t);
if(input_data_size != input.len){
return INVALID_INPUT_LENGTH_ERROR;
}
memcpy(source_image.get_pixels().data(), input.data, input.len);
if (rp.m_status_output)
{
printf("Max threads: %u\n", max_threads);
printf("Supports bc7e.ispc: %u\n", SUPPORT_BC7E);
}
clock_t overall_start_t = clock();
rdo_bc::rdo_bc_encoder encoder;
if (!encoder.init(source_image, rp))
{
fprintf(stderr, "rdo_bc_encoder::init() failed!\n");
return ENCODER_INIT_ERROR;
}
if (rp.m_status_output)
{
if (encoder.get_has_alpha())
printf("Source image has an alpha channel.\n");
else
printf("Source image is opaque.\n");
}
if (!encoder.encode())
{
fprintf(stderr, "rdo_bc_encoder::encode() failed!\n");
return ENCODE_ERROR;
}
clock_t overall_end_t = clock();
if (rp.m_status_output)
printf("Total processing time: %f secs\n", (double)(overall_end_t - overall_start_t) / CLOCKS_PER_SEC);
// Compress the output data losslessly using Deflate
const uint32_t output_data_size = encoder.get_total_blocks_size_in_bytes();
// const uint32_t pre_rdo_comp_size = get_deflate_size(encoder.get_prerdo_blocks(), output_data_size);
// float pre_rdo_lz_bits_per_texel = (pre_rdo_comp_size * 8.0f) / encoder.get_total_texels();
// if (rp.m_status_output)
// {
// printf("Output data size: %u, LZ (Deflate) compressed file size: %u, %3.2f bits/texel\n",
// output_data_size,
// (uint32_t)pre_rdo_comp_size,
// pre_rdo_lz_bits_per_texel);
// }
// const uint32_t comp_size = get_deflate_size(encoder.get_blocks(), output_data_size);
// float lz_bits_per_texel = comp_size * 8.0f / encoder.get_total_texels();
// if (rp.m_status_output)
// printf("RDO output data size: %u, LZ (Deflate) compressed file size: %u, %3.2f bits/texel, savings: %3.2f%%\n", output_data_size, (uint32_t)comp_size, lz_bits_per_texel,
// (lz_bits_per_texel != pre_rdo_lz_bits_per_texel) ? 100.0f - (lz_bits_per_texel * 100.0f) / pre_rdo_lz_bits_per_texel : 0.0f);
if(output_data_size != output.len){
fprintf(stderr, "Output length is %d, expected %d\n", output.len, output_data_size);
return INVALID_OUTPUT_LENGTH_ERROR;
}
// TODO: avoid a copy
memcpy(output.data, encoder.get_blocks(), output_data_size);
return NO_ERROR;
}