ASTC 6x6 HDR/6x6 HDR intermediate update

This commit is contained in:
Richard Geldreich
2025-01-21 11:01:08 -05:00
parent ae0dcd6f62
commit 1ba75b4e34
49 changed files with 26835 additions and 6776 deletions
+8
View File
@@ -836,10 +836,12 @@ void decodeISETritBlock (ISEDecodedResult* dst, int numValues, BitAccessStream&
m[4] = data.getNext(numBits);
deUint32 T7 = data.getNext(1);
#ifndef __EMSCRIPTEN__
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough="
#endif
#endif
switch (numValues)
{
// \note Fall-throughs.
@@ -851,9 +853,11 @@ void decodeISETritBlock (ISEDecodedResult* dst, int numValues, BitAccessStream&
default:
DE_ASSERT(false);
}
#ifndef __EMSCRIPTEN__
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#endif
const deUint32 T = (T7 << 7) | (T56 << 5) | (T4 << 4) | (T23 << 2) | (T01 << 0);
@@ -898,10 +902,12 @@ void decodeISEQuintBlock (ISEDecodedResult* dst, int numValues, BitAccessStream&
m[2] = data.getNext(numBits);
deUint32 Q56 = data.getNext(2);
#ifndef __EMSCRIPTEN__
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough="
#endif
#endif
switch (numValues)
{
// \note Fall-throughs.
@@ -911,9 +917,11 @@ void decodeISEQuintBlock (ISEDecodedResult* dst, int numValues, BitAccessStream&
default:
DE_ASSERT(false);
}
#ifndef __EMSCRIPTEN__
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#endif
const deUint32 Q = (Q56 << 5) | (Q34 << 3) | (Q012 << 0);
+1 -2
View File
@@ -2080,5 +2080,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
*/
+1 -1
View File
@@ -4928,7 +4928,7 @@ static int DecodeTiledLevel(EXRImage* exr_image, const EXRHeader* exr_header,
}
#endif
exr_image->tiles = static_cast<EXRTile*>(
calloc(static_cast<size_t>(num_tiles), sizeof(EXRTile)));
calloc(sizeof(EXRTile), static_cast<size_t>(num_tiles)));
#if TINYEXR_HAS_CXX11 && (TINYEXR_USE_THREAD > 0)
std::vector<std::thread> workers;
+730 -268
View File
File diff suppressed because it is too large Load Diff
+209 -64
View File
@@ -18,10 +18,11 @@
#include "basisu_basis_file.h"
#include "../transcoder/basisu_transcoder.h"
#include "basisu_uastc_enc.h"
#include "basisu_astc_hdr_enc.h"
#include "basisu_uastc_hdr_4x4_enc.h"
#include "basisu_astc_hdr_6x6_enc.h"
#define BASISU_LIB_VERSION 150
#define BASISU_LIB_VERSION_STRING "1.50"
#define BASISU_LIB_VERSION 160
#define BASISU_LIB_VERSION_STRING "1.60"
#ifndef BASISD_SUPPORT_KTX2
#error BASISD_SUPPORT_KTX2 is undefined
@@ -76,6 +77,8 @@ namespace basisu
m_height = 0;
m_basis_rgb_avg_psnr = 0.0f;
m_basis_rgb_avg_log2_psnr = 0.0f;
m_basis_rgba_avg_psnr = 0.0f;
m_basis_a_avg_psnr = 0.0f;
m_basis_luma_709_psnr = 0.0f;
@@ -83,6 +86,7 @@ namespace basisu
m_basis_luma_709_ssim = 0.0f;
m_basis_rgb_avg_bc6h_psnr = 0.0f;
m_basis_rgb_avg_bc6h_log2_psnr = 0.0f;
m_bc7_rgb_avg_psnr = 0.0f;
m_bc7_rgba_avg_psnr = 0.0f;
@@ -105,6 +109,8 @@ namespace basisu
// .basis/.ktx2 compressed (LDR: ETC1S or UASTC statistics, HDR: transcoded BC6H statistics)
float m_basis_rgb_avg_psnr;
float m_basis_rgb_avg_log2_psnr;
float m_basis_rgba_avg_psnr;
float m_basis_a_avg_psnr;
float m_basis_luma_709_psnr;
@@ -113,6 +119,7 @@ namespace basisu
// UASTC HDR only.
float m_basis_rgb_avg_bc6h_psnr;
float m_basis_rgb_avg_bc6h_log2_psnr;
// LDR: BC7 statistics
float m_bc7_rgb_avg_psnr;
@@ -131,6 +138,17 @@ namespace basisu
bool m_opencl_failed;
};
enum class hdr_modes
{
// standard but constrained ASTC HDR 4x4 tex data that can be rapidly transcoded to BC6H
cUASTC_HDR_4X4,
// standard RDO optimized or non-RDO (highest quality) ASTC HDR 6x6 tex data that can be rapidly re-encoded to BC6H
cASTC_HDR_6X6,
// a custom intermediate format based off ASTC HDR that can be rapidly decoded straight to ASTC HDR or re-encoded to BC6H
cASTC_HDR_6X6_INTERMEDIATE,
cTotal
};
template<bool def>
struct bool_param
{
@@ -220,21 +238,23 @@ namespace basisu
m_endpoint_rdo_thresh(BASISU_DEFAULT_ENDPOINT_RDO_THRESH, 0.0f, 1e+10f),
m_mip_scale(1.0f, .000125f, 4.0f),
m_mip_smallest_dimension(1, 1, 16384),
m_max_endpoint_clusters(512),
m_max_selector_clusters(512),
m_quality_level(-1),
m_pack_uastc_flags(cPackUASTCLevelDefault),
m_rdo_uastc_quality_scalar(1.0f, 0.001f, 50.0f),
m_rdo_uastc_dict_size(BASISU_RDO_UASTC_DICT_SIZE_DEFAULT, BASISU_RDO_UASTC_DICT_SIZE_MIN, BASISU_RDO_UASTC_DICT_SIZE_MAX),
m_rdo_uastc_max_smooth_block_error_scale(UASTC_RDO_DEFAULT_SMOOTH_BLOCK_MAX_ERROR_SCALE, 1.0f, 300.0f),
m_rdo_uastc_smooth_block_max_std_dev(UASTC_RDO_DEFAULT_MAX_SMOOTH_BLOCK_STD_DEV, .01f, 65536.0f),
m_rdo_uastc_max_allowed_rms_increase_ratio(UASTC_RDO_DEFAULT_MAX_ALLOWED_RMS_INCREASE_RATIO, .01f, 100.0f),
m_rdo_uastc_skip_block_rms_thresh(UASTC_RDO_DEFAULT_SKIP_BLOCK_RMS_THRESH, .01f, 100.0f),
m_etc1s_max_endpoint_clusters(512),
m_etc1s_max_selector_clusters(512),
m_etc1s_quality_level(-1),
m_pack_uastc_ldr_4x4_flags(cPackUASTCLevelDefault),
m_rdo_uastc_ldr_4x4_quality_scalar(1.0f, 0.001f, 50.0f),
m_rdo_uastc_ldr_4x4_dict_size(BASISU_RDO_UASTC_DICT_SIZE_DEFAULT, BASISU_RDO_UASTC_DICT_SIZE_MIN, BASISU_RDO_UASTC_DICT_SIZE_MAX),
m_rdo_uastc_ldr_4x4_max_smooth_block_error_scale(UASTC_RDO_DEFAULT_SMOOTH_BLOCK_MAX_ERROR_SCALE, 1.0f, 300.0f),
m_rdo_uastc_ldr_4x4_smooth_block_max_std_dev(UASTC_RDO_DEFAULT_MAX_SMOOTH_BLOCK_STD_DEV, .01f, 65536.0f),
m_rdo_uastc_ldr_4x4_max_allowed_rms_increase_ratio(UASTC_RDO_DEFAULT_MAX_ALLOWED_RMS_INCREASE_RATIO, .01f, 100.0f),
m_rdo_uastc_ldr_4x4_skip_block_rms_thresh(UASTC_RDO_DEFAULT_SKIP_BLOCK_RMS_THRESH, .01f, 100.0f),
m_resample_width(0, 1, 16384),
m_resample_height(0, 1, 16384),
m_resample_factor(0.0f, .00125f, 100.0f),
m_ktx2_uastc_supercompression(basist::KTX2_SS_NONE),
m_ktx2_zstd_supercompression_level(6, INT_MIN, INT_MAX),
m_ldr_hdr_upconversion_nit_multiplier(0.0f, 0.0f, basist::MAX_HALF_FLOAT),
m_ldr_hdr_upconversion_black_bias(0.0f, 0.0f, 1.0f),
m_pJob_pool(nullptr)
{
clear();
@@ -243,6 +263,9 @@ namespace basisu
void clear()
{
m_uastc.clear();
m_hdr.clear();
m_hdr_mode = hdr_modes::cUASTC_HDR_4X4;
m_use_opencl.clear();
m_status_output.clear();
@@ -290,24 +313,24 @@ namespace basisu
m_mip_fast.clear();
m_mip_smallest_dimension.clear();
m_max_endpoint_clusters = 0;
m_max_selector_clusters = 0;
m_quality_level = -1;
m_etc1s_max_endpoint_clusters = 0;
m_etc1s_max_selector_clusters = 0;
m_etc1s_quality_level = -1;
m_tex_type = basist::cBASISTexType2D;
m_userdata0 = 0;
m_userdata1 = 0;
m_us_per_frame = 0;
m_pack_uastc_flags = cPackUASTCLevelDefault;
m_rdo_uastc.clear();
m_rdo_uastc_quality_scalar.clear();
m_rdo_uastc_max_smooth_block_error_scale.clear();
m_rdo_uastc_smooth_block_max_std_dev.clear();
m_rdo_uastc_max_allowed_rms_increase_ratio.clear();
m_rdo_uastc_skip_block_rms_thresh.clear();
m_rdo_uastc_favor_simpler_modes_in_rdo_mode.clear();
m_rdo_uastc_multithreading.clear();
m_pack_uastc_ldr_4x4_flags = cPackUASTCLevelDefault;
m_rdo_uastc_ldr_4x4.clear();
m_rdo_uastc_ldr_4x4_quality_scalar.clear();
m_rdo_uastc_ldr_4x4_max_smooth_block_error_scale.clear();
m_rdo_uastc_ldr_4x4_smooth_block_max_std_dev.clear();
m_rdo_uastc_ldr_4x4_max_allowed_rms_increase_ratio.clear();
m_rdo_uastc_ldr_4x4_skip_block_rms_thresh.clear();
m_rdo_uastc_ldr_4x4_favor_simpler_modes_in_rdo_mode.clear();
m_rdo_uastc_ldr_4x4_multithreading.clear();
m_resample_width.clear();
m_resample_height.clear();
@@ -323,19 +346,80 @@ namespace basisu
m_validate_output_data.clear();
m_hdr_ldr_srgb_to_linear_conversion.clear();
m_ldr_hdr_upconversion_srgb_to_linear.clear();
m_hdr_favor_astc.clear();
m_uastc_hdr_4x4_options.init();
m_astc_hdr_6x6_options.clear();
m_ldr_hdr_upconversion_nit_multiplier.clear();
m_ldr_hdr_upconversion_black_bias.clear();
m_pJob_pool = nullptr;
}
// Configures the compressor's mode by setting the proper parameters (which were preserved for backwards compatibility with old code).
void set_format_mode(basist::basis_tex_format m)
{
switch (m)
{
case basist::basis_tex_format::cETC1S:
{
m_hdr = false;
m_uastc = false;
m_hdr_mode = hdr_modes::cUASTC_HDR_4X4; // doesn't matter
break;
}
case basist::basis_tex_format::cUASTC4x4:
{
m_hdr = false;
m_uastc = true;
m_hdr_mode = hdr_modes::cUASTC_HDR_4X4; // doesn't matter
break;
}
case basist::basis_tex_format::cUASTC_HDR_4x4:
{
m_hdr = true;
m_uastc = true;
m_hdr_mode = hdr_modes::cUASTC_HDR_4X4;
break;
}
case basist::basis_tex_format::cASTC_HDR_6x6:
{
m_hdr = true;
m_uastc = true;
m_hdr_mode = hdr_modes::cASTC_HDR_6X6;
break;
}
case basist::basis_tex_format::cASTC_HDR_6x6_INTERMEDIATE:
{
m_hdr = true;
m_uastc = true;
m_hdr_mode = hdr_modes::cASTC_HDR_6X6_INTERMEDIATE;
break;
}
default:
assert(0);
break;
}
}
// By default we generate LDR ETC1S data.
// if m_uastc is true but m_hdr is not true, we generate UASTC 4x4 LDR data (8bpp with or without RDO).
// if m_uastc is true and m_hdr is true, we generate 4x4 or 6x6 HDR data (either standard ASTC, constrained ASTC, RDO ASTC, or intermediate), controlled by m_hdr_mode.
// True to generate UASTC .basis/.KTX2 file data, otherwise ETC1S.
// Should be true for any non-ETC1S format (UASTC 4x4 LDR, UASTC 4x4 HDR, RDO ASTC 6x6 HDR, and ASTC 6x6 HDR intermediate).
bool_param<false> m_uastc;
// Set m_hdr to true to switch to UASTC HDR mode.
// Set m_hdr to true to switch to UASTC HDR mode. m_hdr_mode then controls which format is output.
// m_hdr_mode then controls which format is output (4x4, 6x6, or 6x6 intermediate).
bool_param<false> m_hdr;
// If m_hdr is true, this specifies which mode we operate in (currently UASTC 4x4 HDR or ASTC 6x6 HDR). Defaults to UASTC 4x4 HDR for backwards compatibility.
hdr_modes m_hdr_mode;
bool_param<false> m_use_opencl;
// If m_read_source_images is true, m_source_filenames (and optionally m_source_alpha_filenames) contains the filenames of PNG etc. images to read.
@@ -426,30 +510,31 @@ namespace basisu
bool_param<true> m_mip_fast;
param<int> m_mip_smallest_dimension;
// Codebook size (quality) control.
// If m_quality_level != -1, it controls the quality level. It ranges from [1,255] or [BASISU_QUALITY_MIN, BASISU_QUALITY_MAX].
// ETC1S codebook size (quality) control.
// If m_etc1s_quality_level != -1, it controls the quality level. It ranges from [1,255] or [BASISU_QUALITY_MIN, BASISU_QUALITY_MAX].
// Otherwise m_max_endpoint_clusters/m_max_selector_clusters controls the codebook sizes directly.
uint32_t m_max_endpoint_clusters;
uint32_t m_max_selector_clusters;
int m_quality_level;
uint32_t m_etc1s_max_endpoint_clusters;
uint32_t m_etc1s_max_selector_clusters;
int m_etc1s_quality_level;
// m_tex_type, m_userdata0, m_userdata1, m_framerate - These fields go directly into the Basis file header.
// m_tex_type, m_userdata0, m_userdata1, m_framerate - These fields go directly into the .basis file header.
basist::basis_texture_type m_tex_type;
uint32_t m_userdata0;
uint32_t m_userdata1;
uint32_t m_us_per_frame;
// UASTC LDR 4x4 parameters
// cPackUASTCLevelDefault, etc.
uint32_t m_pack_uastc_flags;
bool_param<false> m_rdo_uastc;
param<float> m_rdo_uastc_quality_scalar;
param<int> m_rdo_uastc_dict_size;
param<float> m_rdo_uastc_max_smooth_block_error_scale;
param<float> m_rdo_uastc_smooth_block_max_std_dev;
param<float> m_rdo_uastc_max_allowed_rms_increase_ratio;
param<float> m_rdo_uastc_skip_block_rms_thresh;
bool_param<true> m_rdo_uastc_favor_simpler_modes_in_rdo_mode;
bool_param<true> m_rdo_uastc_multithreading;
uint32_t m_pack_uastc_ldr_4x4_flags;
bool_param<false> m_rdo_uastc_ldr_4x4;
param<float> m_rdo_uastc_ldr_4x4_quality_scalar;
param<int> m_rdo_uastc_ldr_4x4_dict_size;
param<float> m_rdo_uastc_ldr_4x4_max_smooth_block_error_scale;
param<float> m_rdo_uastc_ldr_4x4_smooth_block_max_std_dev;
param<float> m_rdo_uastc_ldr_4x4_max_allowed_rms_increase_ratio;
param<float> m_rdo_uastc_ldr_4x4_skip_block_rms_thresh;
bool_param<true> m_rdo_uastc_ldr_4x4_favor_simpler_modes_in_rdo_mode;
bool_param<true> m_rdo_uastc_ldr_4x4_multithreading;
param<int> m_resample_width;
param<int> m_resample_height;
@@ -465,13 +550,26 @@ namespace basisu
param<int> m_ktx2_zstd_supercompression_level;
bool_param<false> m_ktx2_srgb_transfer_func;
astc_hdr_codec_options m_uastc_hdr_options;
uastc_hdr_4x4_codec_options m_uastc_hdr_4x4_options;
astc_6x6_hdr::astc_hdr_6x6_global_config m_astc_hdr_6x6_options;
bool_param<false> m_validate_output_data;
// If true, LDR images (such as PNG) will be converted to normalized [0,1] linear light (via a sRGB->Linear conversion) and then processed as HDR.
// Otherwise, LDR images will be processed as HDR as-is.
bool_param<true> m_hdr_ldr_srgb_to_linear_conversion;
// LDR->HDR upconversion parameters.
//
// If true, LDR images (such as PNG) will be converted to normalized [0,1] linear light (via a sRGB->Linear conversion), or absolute luminance (nits or candelas per meter squared), and then processed as HDR.
// Otherwise, LDR images are assumed to already be in linear light (i.e. they don't use the sRGB transfer function).
bool_param<true> m_ldr_hdr_upconversion_srgb_to_linear;
// m_ldr_hdr_upconversion_nit_multiplier is only used when loading SDR/LDR images and compressing to an HDR output format.
// By default m_ldr_hdr_upconversion_nit_multiplier is 0. It's an override for the default.
// When loading LDR images, a default multiplier of 1.0 will be used in UASTC 4x4 HDR mode. Partially for backwards compatibility with previous library releases, and also because it doesn't really matter with this encoder what the multiplier is.
// With the 6x6 HDR encoder it does matter because it expects inputs in absolute nits, so the LDR upconversion luminance multiplier default will be 100 nits. (Most SDR monitors were/are 80-100 nits or so.)
param<float> m_ldr_hdr_upconversion_nit_multiplier;
// The optional sRGB space bias to use during LDR->HDR upconversion. Should be between [0,.49] or so. Only applied on black (0.0) color components.
// Defaults to no bias (0.0f).
param<float> m_ldr_hdr_upconversion_black_bias;
// If true, ASTC HDR quality is favored more than BC6H quality. Otherwise it's a rough balance.
bool_param<false> m_hdr_favor_astc;
@@ -526,8 +624,10 @@ namespace basisu
private:
basis_compressor_params m_params;
opencl_context_ptr m_pOpenCL_context;
basist::basis_tex_format m_fmt_mode;
basisu::vector<image> m_slice_images;
basisu::vector<imagef> m_slice_images_hdr;
@@ -543,6 +643,7 @@ namespace basisu
basisu_frontend m_frontend;
// These are 4x4 blocks.
pixel_block_vec m_source_blocks;
pixel_block_hdr_vec m_source_blocks_hdr;
@@ -572,6 +673,15 @@ namespace basisu
basisu::vector<gpu_image> m_uastc_slice_textures;
basisu_backend_output m_uastc_backend_output;
// The amount the HDR input has to be scaled up in case it had to be rescaled to fit into half floats.
float m_hdr_image_scale;
// The upconversion multiplier used to load LDR images in HDR mode.
float m_ldr_to_hdr_upconversion_nit_multiplier;
// True if any loaded source images were LDR and upconverted to HDR.
bool m_upconverted_any_ldr_images;
bool m_any_source_image_has_alpha;
bool m_opencl_failed;
@@ -588,14 +698,48 @@ namespace basisu
bool create_basis_file_and_transcode();
bool write_hdr_debug_images(const char* pBasename, const imagef& img, uint32_t width, uint32_t height);
bool write_output_files_and_compute_stats();
error_code encode_slices_to_uastc_hdr();
error_code encode_slices_to_uastc();
error_code encode_slices_to_astc_6x6_hdr();
error_code encode_slices_to_uastc_4x4_hdr();
error_code encode_slices_to_uastc_4x4_ldr();
bool generate_mipmaps(const imagef& img, basisu::vector<imagef>& mips, bool has_alpha);
bool generate_mipmaps(const image &img, basisu::vector<image> &mips, bool has_alpha);
bool validate_texture_type_constraints();
bool validate_ktx2_constraints();
void get_dfd(uint8_vec& dfd, const basist::ktx2_header& hdr);
bool get_dfd(uint8_vec& dfd, const basist::ktx2_header& hdr);
bool create_ktx2_file();
void pick_format_mode();
uint32_t get_block_width() const
{
if (m_params.m_hdr)
{
switch (m_params.m_hdr_mode)
{
case hdr_modes::cASTC_HDR_6X6:
case hdr_modes::cASTC_HDR_6X6_INTERMEDIATE:
return 6;
default:
break;
}
}
return 4;
}
uint32_t get_block_height() const
{
if (m_params.m_hdr)
{
switch (m_params.m_hdr_mode)
{
case hdr_modes::cASTC_HDR_6X6:
case hdr_modes::cASTC_HDR_6X6_INTERMEDIATE:
return 6;
default:
break;
}
}
return 4;
}
};
// Alternative simple C-style wrapper API around the basis_compressor class.
@@ -628,21 +772,19 @@ namespace basisu
cFlagKTX2UASTCSuperCompression = 1 << 12, // use KTX2 Zstd supercompression on UASTC files
cFlagSRGB = 1 << 13, // input texture is sRGB, use perceptual colorspace metrics, also use sRGB filtering during mipmap gen, and also sets KTX2 output transfer func to sRGB
cFlagGenMipsClamp = 1 << 14, // generate mipmaps with clamp addressing
cFlagGenMipsWrap = 1 << 15, // generate mipmaps with wrap addressing
cFlagGenMipsClamp = 1 << 14, // generate mipmaps with clamp addressing
cFlagGenMipsWrap = 1 << 15, // generate mipmaps with wrap addressing
cFlagYFlip = 1 << 16, // flip source image on Y axis before compression
cFlagYFlip = 1 << 16, // flip source image on Y axis before compression
cFlagUASTC = 1 << 17, // use UASTC compression vs. ETC1S
cFlagUASTCRDO = 1 << 18, // use RDO postprocessing when generating UASTC files (must set uastc_rdo_quality to the quality scalar)
cFlagUASTCRDO = 1 << 17, // use RDO postprocessing when generating UASTC files (must set uastc_rdo_quality to the quality scalar)
cFlagPrintStats = 1 << 19, // print image stats to stdout
cFlagPrintStatus = 1 << 20, // print status to stdout
cFlagPrintStats = 1 << 18, // print image stats to stdout
cFlagPrintStatus = 1 << 19, // print status to stdout
cFlagHDR = 1 << 21, // Force encoder into HDR mode, even if source image is LDR.
cFlagHDRLDRImageSRGBToLinearConversion = 1 << 22, // In HDR mode, convert LDR source images to linear before encoding.
cFlagDebugImages = 1 << 23 // enable status output
cFlagDebugImages = 1 << 20, // enable status output
cFlagREC2020 = 1 << 21 // ASTC 6x6 modes: treat input as REC 2020 vs. the default 709
};
// This function accepts an array of source images.
@@ -652,6 +794,7 @@ namespace basisu
// basisu_encoder_init() MUST be called first!
// LDR version. To compress the LDR source image as HDR: Use the cFlagHDR flag.
void* basis_compress(
basist::basis_tex_format mode,
const basisu::vector<image> &source_images,
uint32_t flags_and_quality, float uastc_rdo_quality,
size_t* pSize,
@@ -660,14 +803,16 @@ namespace basisu
// HDR-only version.
// Important: The returned block MUST be manually freed using basis_free_data().
void* basis_compress(
basist::basis_tex_format mode,
const basisu::vector<imagef>& source_images_hdr,
uint32_t flags_and_quality,
uint32_t flags_and_quality, float lambda,
size_t* pSize,
image_stats* pStats = nullptr);
// This function only accepts a single LDR source image. It's just a wrapper for basis_compress() above.
// Important: The returned block MUST be manually freed using basis_free_data().
void* basis_compress(
basist::basis_tex_format mode,
const uint8_t* pImageRGBA, uint32_t width, uint32_t height, uint32_t pitch_in_pixels,
uint32_t flags_and_quality, float uastc_rdo_quality,
size_t* pSize,
+327 -45
View File
@@ -21,7 +21,9 @@
#include "jpgd.h"
#include "pvpngreader.h"
#include "basisu_opencl.h"
#include "basisu_astc_hdr_enc.h"
#include "basisu_uastc_hdr_4x4_enc.h"
#include "basisu_astc_hdr_6x6_enc.h"
#include <vector>
#ifndef TINYEXR_USE_ZFP
@@ -50,10 +52,13 @@ namespace basisu
{
uint64_t interval_timer::g_init_ticks, interval_timer::g_freq;
double interval_timer::g_timer_freq;
#if BASISU_SUPPORT_SSE
bool g_cpu_supports_sse41;
#endif
fast_linear_to_srgb g_fast_linear_to_srgb;
uint8_t g_hamming_dist[256] =
{
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
@@ -178,7 +183,7 @@ namespace basisu
bool g_library_initialized;
std::mutex g_encoder_init_mutex;
// Encoder library initialization (just call once at startup)
bool basisu_encoder_init(bool use_opencl, bool opencl_force_serialization)
{
@@ -188,7 +193,7 @@ namespace basisu
return true;
detect_sse41();
basist::basisu_transcoder_init();
pack_etc1_solid_color_init();
//uastc_init();
@@ -204,6 +209,7 @@ namespace basisu
astc_hdr_enc_init();
basist::bc6h_enc_init();
astc_6x6_hdr::global_init();
g_library_initialized = true;
return true;
@@ -218,15 +224,40 @@ namespace basisu
void error_vprintf(const char* pFmt, va_list args)
{
char buf[8192];
const uint32_t BUF_SIZE = 256;
char buf[BUF_SIZE];
#ifdef _WIN32
vsprintf_s(buf, sizeof(buf), pFmt, args);
#else
vsnprintf(buf, sizeof(buf), pFmt, args);
#endif
va_list args_copy;
va_copy(args_copy, args);
int total_chars = vsnprintf(buf, sizeof(buf), pFmt, args_copy);
va_end(args_copy);
fprintf(stderr, "ERROR: %s", buf);
if (total_chars < 0)
{
assert(0);
return;
}
if (total_chars >= (int)BUF_SIZE)
{
basisu::vector<char> var_buf(total_chars + 1);
va_copy(args_copy, args);
int total_chars_retry = vsnprintf(var_buf.data(), var_buf.size(), pFmt, args_copy);
va_end(args_copy);
if (total_chars_retry < 0)
{
assert(0);
return;
}
fprintf(stderr, "ERROR: %s", var_buf.data());
}
else
{
fprintf(stderr, "ERROR: %s", buf);
}
}
void error_printf(const char *pFmt, ...)
@@ -237,6 +268,18 @@ namespace basisu
va_end(args);
}
#if defined(_WIN32)
void platform_sleep(uint32_t ms)
{
Sleep(ms);
}
#else
void platform_sleep(uint32_t ms)
{
// TODO
}
#endif
#if defined(_WIN32)
inline void query_counter(timer_ticks* pTicks)
{
@@ -334,6 +377,8 @@ namespace basisu
return ticks * g_timer_freq;
}
// Note this is linear<->sRGB, NOT REC709 which uses slightly different equations/transfer functions.
// However the gamuts/white points of REC709 and sRGB are the same.
float linear_to_srgb(float l)
{
assert(l >= 0.0f && l <= 1.0f);
@@ -342,7 +387,7 @@ namespace basisu
else
return saturate(1.055f * powf(l, 1.0f / 2.4f) - .055f);
}
float srgb_to_linear(float s)
{
assert(s >= 0.0f && s <= 1.0f);
@@ -430,6 +475,7 @@ namespace basisu
uint32_t width = 0, height = 0, num_chans = 0;
void* pImage = pv_png::load_png(pBuf, buf_size, 4, width, height, num_chans);
if (!pImage)
{
error_printf("pv_png::load_png failed while loading image \"%s\"\n", pFilename);
@@ -469,6 +515,26 @@ namespace basisu
return true;
}
bool load_jpg(const uint8_t* pBuf, size_t buf_size, image& img)
{
if (buf_size > INT_MAX)
{
assert(0);
return false;
}
int width = 0, height = 0, actual_comps = 0;
uint8_t* pImage_data = jpgd::decompress_jpeg_image_from_memory(pBuf, (int)buf_size, &width, &height, &actual_comps, 4, jpgd::jpeg_decoder::cFlagLinearChromaFiltering);
if (!pImage_data)
return false;
img.init(pImage_data, width, height, 4);
free(pImage_data);
return true;
}
bool load_image(const char* pFilename, image& img)
{
std::string ext(string_get_extension(std::string(pFilename)));
@@ -490,7 +556,7 @@ namespace basisu
return false;
}
static void convert_ldr_to_hdr_image(imagef &img, const image &ldr_img, bool ldr_srgb_to_linear)
static void convert_ldr_to_hdr_image(imagef &img, const image &ldr_img, bool ldr_srgb_to_linear, float linear_nit_multiplier = 1.0f, float ldr_black_bias = 0.0f)
{
img.resize(ldr_img.get_width(), ldr_img.get_height());
@@ -503,23 +569,41 @@ namespace basisu
vec4F& d = img(x, y);
if (ldr_srgb_to_linear)
{
// TODO: Multiply by 100-200 nits?
d[0] = srgb_to_linear(c[0] * (1.0f / 255.0f));
d[1] = srgb_to_linear(c[1] * (1.0f / 255.0f));
d[2] = srgb_to_linear(c[2] * (1.0f / 255.0f));
float r = (float)c[0];
float g = (float)c[1];
float b = (float)c[2];
if (ldr_black_bias > 0.0f)
{
// ASTC HDR is noticeably weaker dealing with blocks containing some pixels with components set to 0.
// Add a very slight bias less than .5 to avoid this difficulity. When the HDR image is mapped to SDR sRGB and rounded back to 8-bits, this bias will still result in zero.
// (FWIW, in reality, a physical monitor would be unlikely to have a perfectly zero black level.)
// This is purely optional and on most images it doesn't matter visually.
if (r == 0.0f)
r = ldr_black_bias;
if (g == 0.0f)
g = ldr_black_bias;
if (b == 0.0f)
b = ldr_black_bias;
}
// Compute how much linear light would be emitted by a SDR 80-100 nit monitor.
d[0] = srgb_to_linear(r * (1.0f / 255.0f)) * linear_nit_multiplier;
d[1] = srgb_to_linear(g * (1.0f / 255.0f)) * linear_nit_multiplier;
d[2] = srgb_to_linear(b * (1.0f / 255.0f)) * linear_nit_multiplier;
}
else
{
d[0] = c[0] * (1.0f / 255.0f);
d[1] = c[1] * (1.0f / 255.0f);
d[2] = c[2] * (1.0f / 255.0f);
d[0] = c[0] * (1.0f / 255.0f) * linear_nit_multiplier;
d[1] = c[1] * (1.0f / 255.0f) * linear_nit_multiplier;
d[2] = c[2] * (1.0f / 255.0f) * linear_nit_multiplier;
}
d[3] = c[3] * (1.0f / 255.0f);
}
}
}
bool load_image_hdr(const void* pMem, size_t mem_size, imagef& img, uint32_t width, uint32_t height, hdr_image_type img_type, bool ldr_srgb_to_linear)
bool load_image_hdr(const void* pMem, size_t mem_size, imagef& img, uint32_t width, uint32_t height, hdr_image_type img_type, bool ldr_srgb_to_linear, float linear_nit_multiplier, float ldr_black_bias)
{
if ((!pMem) || (!mem_size))
{
@@ -583,13 +667,22 @@ namespace basisu
break;
}
case hdr_image_type::cHITJPGImage:
{
image ldr_img;
if (!load_jpg(static_cast<const uint8_t*>(pMem), mem_size, ldr_img))
return false;
convert_ldr_to_hdr_image(img, ldr_img, ldr_srgb_to_linear, linear_nit_multiplier, ldr_black_bias);
break;
}
case hdr_image_type::cHITPNGImage:
{
image ldr_img;
if (!load_png(static_cast<const uint8_t *>(pMem), mem_size, ldr_img))
return false;
convert_ldr_to_hdr_image(img, ldr_img, ldr_srgb_to_linear);
convert_ldr_to_hdr_image(img, ldr_img, ldr_srgb_to_linear, linear_nit_multiplier, ldr_black_bias);
break;
}
case hdr_image_type::cHITEXRImage:
@@ -617,8 +710,21 @@ namespace basisu
return true;
}
bool is_image_filename_hdr(const char *pFilename)
{
std::string ext(string_get_extension(std::string(pFilename)));
if (ext.length() == 0)
return false;
const char* pExt = ext.c_str();
return ((strcasecmp(pExt, "hdr") == 0) || (strcasecmp(pExt, "exr") == 0));
}
bool load_image_hdr(const char* pFilename, imagef& img, bool ldr_srgb_to_linear)
// TODO: move parameters to struct, add a HDR clean flag to eliminate NaN's/Inf's
bool load_image_hdr(const char* pFilename, imagef& img, bool ldr_srgb_to_linear, float linear_nit_multiplier, float ldr_black_bias)
{
std::string ext(string_get_extension(std::string(pFilename)));
@@ -649,7 +755,7 @@ namespace basisu
if (!load_image(pFilename, ldr_img))
return false;
convert_ldr_to_hdr_image(img, ldr_img, ldr_srgb_to_linear);
convert_ldr_to_hdr_image(img, ldr_img, ldr_srgb_to_linear, linear_nit_multiplier, ldr_black_bias);
}
return true;
@@ -1014,7 +1120,7 @@ namespace basisu
return false;
}
if ((src_w == dst_w) && (src_h == dst_h))
if ((src_w == dst_w) && (src_h == dst_h) && (filter_scale == 1.0f))
{
dst = src;
return true;
@@ -1664,7 +1770,7 @@ namespace basisu
uint32_t a = max_index / num_syms, b = max_index % num_syms;
const uint32_t ofs = m_entries_picked.size();
const size_t ofs = m_entries_picked.size();
m_entries_picked.push_back(a);
m_entries_picked.push_back(b);
@@ -2014,6 +2120,34 @@ namespace basisu
m_psnr = m_rms ? (float)clamp<double>(log10(255.0 / m_rms) * 20.0f, 0.0f, 100.0f) : 100.0f;
}
void print_image_metrics(const image& a, const image& b)
{
image_metrics im;
im.calc(a, b, 0, 3);
im.print("RGB ");
im.calc(a, b, 0, 4);
im.print("RGBA ");
im.calc(a, b, 0, 1);
im.print("R ");
im.calc(a, b, 1, 1);
im.print("G ");
im.calc(a, b, 2, 1);
im.print("B ");
im.calc(a, b, 3, 1);
im.print("A ");
im.calc(a, b, 0, 0);
im.print("Y 709 ");
im.calc(a, b, 0, 0, true, true);
im.print("Y 601 ");
}
void fill_buffer_with_random_bytes(void *pBuf, size_t size, uint32_t seed)
{
rand r(seed);
@@ -2091,9 +2225,11 @@ namespace basisu
}
job_pool::job_pool(uint32_t num_threads) :
m_num_active_jobs(0),
m_kill_flag(false)
m_num_active_jobs(0)
{
m_kill_flag.store(false);
m_num_active_workers.store(0);
assert(num_threads >= 1U);
debug_printf("job_pool::job_pool: %u total threads\n", num_threads);
@@ -2112,11 +2248,23 @@ namespace basisu
debug_printf("job_pool::~job_pool\n");
// Notify all workers that they need to die right now.
m_kill_flag = true;
m_kill_flag.store(true);
m_has_work.notify_all();
// Wait for all workers to die.
#ifdef __EMSCRIPTEN__
for ( ; ; )
{
if (m_num_active_workers.load() <= 0)
break;
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
// At this point all worker threads should be exiting or exited.
// We could call detach(), but this seems to just call join() anyway.
#endif
// Wait for all worker threads to exit.
for (uint32_t i = 0; i < m_threads.size(); i++)
m_threads[i].join();
}
@@ -2169,13 +2317,26 @@ namespace basisu
}
// The queue is empty, now wait for all active jobs to finish up.
#ifndef __EMSCRIPTEN__
m_no_more_jobs.wait(lock, [this]{ return !m_num_active_jobs; } );
#else
// Avoid infinite blocking
for (; ; )
{
if (m_no_more_jobs.wait_for(lock, std::chrono::milliseconds(50), [this] { return !m_num_active_jobs; }))
{
break;
}
}
#endif
}
void job_pool::job_thread(uint32_t index)
{
BASISU_NOTE_UNUSED(index);
//debug_printf("job_pool::job_thread: starting %u\n", index);
m_num_active_workers.fetch_add(1);
while (true)
{
@@ -2211,6 +2372,8 @@ namespace basisu
m_no_more_jobs.notify_all();
}
m_num_active_workers.fetch_add(-1);
//debug_printf("job_pool::job_thread: exiting\n");
}
@@ -3325,7 +3488,7 @@ namespace basisu
return true;
}
bool write_exr(const char* pFilename, imagef& img, uint32_t n_chans, uint32_t flags)
bool write_exr(const char* pFilename, const imagef& img, uint32_t n_chans, uint32_t flags)
{
assert((n_chans == 1) || (n_chans == 3) || (n_chans == 4));
@@ -3494,11 +3657,14 @@ namespace basisu
// Very basic global Reinhard tone mapping, output converted to sRGB with no dithering, alpha is carried through unchanged.
// Only used for debugging/development.
void tonemap_image_reinhard(image &ldr_img, const imagef &hdr_img, float exposure)
void tonemap_image_reinhard(image &ldr_img, const imagef &hdr_img, float exposure, bool add_noise, bool per_component, bool luma_scaling)
{
uint32_t width = hdr_img.get_width(), height = hdr_img.get_height();
ldr_img.resize(width, height);
rand r;
r.seed(128);
for (uint32_t y = 0; y < height; y++)
{
@@ -3506,32 +3672,84 @@ namespace basisu
{
vec4F c(hdr_img(x, y));
for (uint32_t t = 0; t < 3; t++)
if (per_component)
{
if (c[t] <= 0.0f)
for (uint32_t t = 0; t < 3; t++)
{
c[t] = 0.0f;
if (c[t] <= 0.0f)
{
c[t] = 0.0f;
}
else
{
c[t] *= exposure;
c[t] = c[t] / (1.0f + c[t]);
}
}
else
}
else
{
c[0] *= exposure;
c[1] *= exposure;
c[2] *= exposure;
const float L = 0.2126f * c[0] + 0.7152f * c[1] + 0.0722f * c[2];
float Lmapped = 0.0f;
if (L > 0.0f)
{
c[t] *= exposure;
c[t] = c[t] / (1.0f + c[t]);
//Lmapped = L / (1.0f + L);
//Lmapped /= L;
Lmapped = 1.0f / (1.0f + L);
}
c[0] = c[0] * Lmapped;
c[1] = c[1] * Lmapped;
c[2] = c[2] * Lmapped;
if (luma_scaling)
{
// Keeps the ratio of r/g/b intact
float m = maximum(c[0], c[1], c[2]);
if (m > 1.0f)
{
c /= m;
}
}
}
c.clamp(0.0f, 1.0f);
c[0] = linear_to_srgb(c[0]) * 255.0f;
c[1] = linear_to_srgb(c[1]) * 255.0f;
c[2] = linear_to_srgb(c[2]) * 255.0f;
c[3] = c[3] * 255.0f;
color_rgba& o = ldr_img(x, y);
o[0] = (uint8_t)std::round(c[0]);
o[1] = (uint8_t)std::round(c[1]);
o[2] = (uint8_t)std::round(c[2]);
o[3] = (uint8_t)std::round(c[3]);
if (add_noise)
{
c[0] = linear_to_srgb(c[0]) * 255.0f;
c[1] = linear_to_srgb(c[1]) * 255.0f;
c[2] = linear_to_srgb(c[2]) * 255.0f;
const float NOISE_AMP = .5f;
c[0] += r.frand(-NOISE_AMP, NOISE_AMP);
c[1] += r.frand(-NOISE_AMP, NOISE_AMP);
c[2] += r.frand(-NOISE_AMP, NOISE_AMP);
c.clamp(0.0f, 255.0f);
o[0] = (uint8_t)fast_roundf_int(c[0]);
o[1] = (uint8_t)fast_roundf_int(c[1]);
o[2] = (uint8_t)fast_roundf_int(c[2]);
o[3] = (uint8_t)fast_roundf_int(c[3]);
}
else
{
o[0] = g_fast_linear_to_srgb.convert(c[0]);
o[1] = g_fast_linear_to_srgb.convert(c[1]);
o[2] = g_fast_linear_to_srgb.convert(c[2]);
o[3] = (uint8_t)fast_roundf_int(c[3]);
}
}
}
}
@@ -3692,5 +3910,69 @@ namespace basisu
return true;
}
bool tonemap_image_compressive2(image& dst_img, const imagef& hdr_test_img)
{
const uint32_t width = hdr_test_img.get_width();
const uint32_t height = hdr_test_img.get_height();
dst_img.resize(width, height);
dst_img.set_all(color_rgba(0, 0, 0, 255));
basisu::vector<basist::half_float> half_img(width * 3 * height);
uint32_t low_h = UINT32_MAX, high_h = 0;
for (uint32_t y = 0; y < height; y++)
{
for (uint32_t x = 0; x < width; x++)
{
const vec4F& p = hdr_test_img(x, y);
for (uint32_t i = 0; i < 3; i++)
{
float f = p[i];
if (std::isnan(f) || std::isinf(f))
f = 0.0f;
else if (f < 0.0f)
f = 0.0f;
else if (f > basist::MAX_HALF_FLOAT)
f = basist::MAX_HALF_FLOAT;
uint32_t h = basist::float_to_half(f);
low_h = minimum(low_h, h);
high_h = maximum(high_h, h);
half_img[(x + y * width) * 3 + i] = (basist::half_float)h;
} // i
} // x
} // y
if (low_h == high_h)
return false;
for (uint32_t y = 0; y < height; y++)
{
for (uint32_t x = 0; x < width; x++)
{
for (uint32_t i = 0; i < 3; i++)
{
basist::half_float h = half_img[(x + y * width) * 3 + i];
float f = (float)(h - low_h) / (float)(high_h - low_h);
int iv = basisu::clamp<int>((int)std::round(f * 255.0f), 0, 255);
dst_img(x, y)[i] = (uint8_t)iv;
} // i
} // x
} // y
return true;
}
} // namespace basisu
+432 -151
View File
@@ -64,8 +64,19 @@ namespace basisu
void error_vprintf(const char* pFmt, va_list args);
void error_printf(const char *pFmt, ...);
// Helpers
template <typename... Args>
inline void fmt_error_printf(const char* pFmt, Args&&... args)
{
std::string res;
if (!fmt_variants(res, pFmt, fmt_variant_vec{ fmt_variant(std::forward<Args>(args))... }))
return;
error_printf("%s", res.c_str());
}
void platform_sleep(uint32_t ms);
// Helpers
inline uint8_t clamp255(int32_t i)
{
return (uint8_t)((i & 0xFFFFFF00U) ? (~(i >> 31)) : i);
@@ -98,6 +109,17 @@ namespace basisu
return (uint8_t)((v + (v >> 8)) >> 8);
}
inline int fast_roundf_int(float x)
{
return (x >= 0.0f) ? (int)(x + 0.5f) : (int)(x - 0.5f);
}
inline int fast_floorf_int(float x)
{
int xi = (int)x; // Truncate towards zero
return ((x < 0.0f) && (x != (float)xi)) ? (xi - 1) : xi;
}
inline uint64_t read_bits(const uint8_t* pBuf, uint32_t& bit_offset, uint32_t codesize)
{
assert(codesize <= 64);
@@ -168,6 +190,15 @@ namespace basisu
}
bool string_begins_with(const std::string& str, const char* pPhrase);
// Case sensitive, returns -1 if can't find
inline int string_find_first(const std::string& str, const char* pPhrase)
{
size_t res = str.find(pPhrase, 0);
if (res == std::string::npos)
return -1;
return (int)res;
}
// Hashing
@@ -209,12 +240,23 @@ namespace basisu
template <typename Key>
struct bit_hasher
{
std::size_t operator()(const Key& k) const
inline std::size_t operator()(const Key& k) const
{
return hash_hsieh(reinterpret_cast<const uint8_t *>(&k), sizeof(k));
}
};
struct string_hasher
{
inline std::size_t operator()(const std::string& k) const
{
size_t l = k.size();
if (!l)
return 0;
return hash_hsieh(reinterpret_cast<const uint8_t*>(k.c_str()), l);
}
};
class running_stat
{
public:
@@ -297,7 +339,7 @@ namespace basisu
};
// Linear algebra
template <uint32_t N, typename T>
class vec
{
@@ -318,7 +360,7 @@ namespace basisu
inline vec(const vec &other) { for (uint32_t i = 0; i < N; i++) m_v[i] = other.m_v[i]; }
template <uint32_t OtherN, typename OtherT> inline vec(const vec<OtherN, OtherT> &other) { set(other); }
inline T operator[](uint32_t i) const { assert(i < N); return m_v[i]; }
inline const T& operator[](uint32_t i) const { assert(i < N); return m_v[i]; }
inline T &operator[](uint32_t i) { assert(i < N); return m_v[i]; }
inline T getX() const { return m_v[0]; }
@@ -327,6 +369,7 @@ namespace basisu
inline T getW() const { static_assert(N >= 4, "N too small"); return m_v[3]; }
inline bool operator==(const vec &rhs) const { for (uint32_t i = 0; i < N; i++) if (m_v[i] != rhs.m_v[i]) return false; return true; }
inline bool operator!=(const vec& rhs) const { return !(*this == rhs); }
inline bool operator<(const vec &rhs) const { for (uint32_t i = 0; i < N; i++) { if (m_v[i] < rhs.m_v[i]) return true; else if (m_v[i] != rhs.m_v[i]) return false; } return false; }
inline void set_zero() { for (uint32_t i = 0; i < N; i++) m_v[i] = 0; }
@@ -433,6 +476,8 @@ namespace basisu
inline vec &normalize_in_place() { T len = length(); if (len != 0.0f) *this *= (1.0f / len); return *this; }
inline vec get_normalized() const { vec res(*this); res.normalize_in_place(); return res; }
inline vec &clamp(T l, T h)
{
for (uint32_t i = 0; i < N; i++)
@@ -440,6 +485,14 @@ namespace basisu
return *this;
}
static vec component_mul(const vec& a, const vec& b)
{
vec res;
for (uint32_t i = 0; i < N; i++)
res[i] = a[i] * b[i];
return res;
}
static vec component_min(const vec& a, const vec& b)
{
vec res;
@@ -455,6 +508,14 @@ namespace basisu
res[i] = maximum(a[i], b[i]);
return res;
}
static vec lerp(const vec& a, const vec& b, float s)
{
vec res;
for (uint32_t i = 0; i < N; i++)
res[i] = basisu::lerp(a[i], b[i], s);
return res;
}
};
typedef vec<4, double> vec4D;
@@ -462,12 +523,17 @@ namespace basisu
typedef vec<2, double> vec2D;
typedef vec<1, double> vec1D;
typedef vec<6, float> vec6F;
typedef vec<5, float> vec5F;
typedef vec<4, float> vec4F;
typedef vec<3, float> vec3F;
typedef vec<2, float> vec2F;
typedef vec<1, float> vec1F;
typedef vec<16, float> vec16F;
template<uint32_t N, typename T> struct bitwise_copyable< vec<N, T> > { enum { cFlag = true }; };
template<uint32_t N, typename T> struct bitwise_movable< vec<N, T> > { enum { cFlag = true }; };
template <uint32_t Rows, uint32_t Cols, typename T>
class matrix
@@ -514,6 +580,9 @@ namespace basisu
}
};
template<uint32_t R, uint32_t C, typename T> struct bitwise_copyable< matrix<R, C, T> > { enum { cFlag = true }; };
template<uint32_t R, uint32_t C, typename T> struct bitwise_movable< matrix<R, C, T> > { enum { cFlag = true }; };
template<uint32_t N, typename VectorType>
inline VectorType compute_pca_from_covar(matrix<N, N, float> &cmatrix)
{
@@ -759,6 +828,8 @@ namespace basisu
std::atomic<bool> m_kill_flag;
std::atomic<int> m_num_active_workers;
void job_thread(uint32_t index);
};
@@ -962,6 +1033,9 @@ namespace basisu
inline int get_709_luma() const { return (13938U * m_comps[0] + 46869U * m_comps[1] + 4729U * m_comps[2] + 32768U) >> 16U; }
inline int get_luma(bool luma_601) const { return luma_601 ? get_601_luma() : get_709_luma(); }
inline uint32_t get_bgra_uint32() const { return b | (g << 8) | (r << 16) | (a << 24); }
inline uint32_t get_rgba_uint32() const { return r | (g << 8) | (b << 16) | (a << 24); }
inline basist::color32 get_color32() const
{
return basist::color32(r, g, b, a);
@@ -1135,23 +1209,7 @@ namespace basisu
return true;
}
inline std::string string_format(const char* pFmt, ...)
{
char buf[2048];
va_list args;
va_start(args, pFmt);
#ifdef _WIN32
vsprintf_s(buf, sizeof(buf), pFmt, args);
#else
vsnprintf(buf, sizeof(buf), pFmt, args);
#endif
va_end(args);
return std::string(buf);
}
inline std::string string_tolower(const std::string& s)
{
std::string result(s);
@@ -1710,7 +1768,7 @@ namespace basisu
// This SSE function takes pointers to void types, so do some sanity checks.
assert(sizeof(TrainingVectorType) == sizeof(float) * 16);
assert(sizeof(training_vec_with_weight) == sizeof(std::pair<vec16F, uint64_t>));
update_covar_matrix_16x16_sse41(node.m_training_vecs.size(), m_training_vecs.data(), &node.m_origin, node.m_training_vecs.data(), &cmatrix);
update_covar_matrix_16x16_sse41(node.m_training_vecs.size_u32(), m_training_vecs.data(), &node.m_origin, node.m_training_vecs.data(), &cmatrix);
#endif
}
@@ -2019,9 +2077,7 @@ namespace basisu
for (uint32_t thread_iter = 0; thread_iter < max_threads; thread_iter++)
{
#ifndef __EMSCRIPTEN__
pJob_pool->add_job( [thread_iter, &local_clusters, &local_parent_clusters, &success_flags, &quantizers, &initial_codebook, &q, &limit_clusterizers, &max_codebook_size, &max_threads, &max_parent_codebook_size] {
#endif
Quantizer& lq = quantizers[thread_iter];
uint_vec& cluster_indices = initial_codebook[thread_iter];
@@ -2062,15 +2118,11 @@ namespace basisu
}
}
#ifndef __EMSCRIPTEN__
} );
#endif
} // thread_iter
#ifndef __EMSCRIPTEN__
pJob_pool->wait_for_all();
#endif
uint32_t total_clusters = 0, total_parent_clusters = 0;
@@ -2353,6 +2405,48 @@ namespace basisu
{
}
bitwise_coder(const bitwise_coder& other) :
m_bytes(other.m_bytes),
m_bit_buffer(other.m_bit_buffer),
m_bit_buffer_size(other.m_bit_buffer_size),
m_total_bits(other.m_total_bits)
{
}
bitwise_coder(bitwise_coder&& other) :
m_bytes(std::move(other.m_bytes)),
m_bit_buffer(other.m_bit_buffer),
m_bit_buffer_size(other.m_bit_buffer_size),
m_total_bits(other.m_total_bits)
{
}
bitwise_coder& operator= (const bitwise_coder& rhs)
{
if (this == &rhs)
return *this;
m_bytes = rhs.m_bytes;
m_bit_buffer = rhs.m_bit_buffer;
m_bit_buffer_size = rhs.m_bit_buffer_size;
m_total_bits = rhs.m_total_bits;
return *this;
}
bitwise_coder& operator= (bitwise_coder&& rhs)
{
if (this == &rhs)
return *this;
m_bytes = std::move(rhs.m_bytes);
m_bit_buffer = rhs.m_bit_buffer;
m_bit_buffer_size = rhs.m_bit_buffer_size;
m_total_bits = rhs.m_total_bits;
return *this;
}
inline void clear()
{
clear_vector(m_bytes);
@@ -2370,8 +2464,12 @@ namespace basisu
}
inline const uint8_vec &get_bytes() const { return m_bytes; }
inline uint8_vec& get_bytes() { return m_bytes; }
inline void reserve(uint32_t size) { m_bytes.reserve(size); }
inline uint64_t get_total_bits() const { return m_total_bits; }
inline uint32_t get_total_bits_u32() const { assert(m_total_bits <= UINT32_MAX); return static_cast<uint32_t>(m_total_bits); }
inline void clear_total_bits() { m_total_bits = 0; }
inline void init(uint32_t reserve_size = 1024)
@@ -2495,16 +2593,27 @@ namespace basisu
}
uint32_t emit_huffman_table(const huffman_encoding_table &tab);
void append(const bitwise_coder& other)
{
for (uint32_t i = 0; i < other.m_bytes.size(); i++)
put_bits(other.m_bytes[i], 8);
if (other.m_bit_buffer_size)
put_bits(other.m_bit_buffer, other.m_bit_buffer_size);
}
private:
uint8_vec m_bytes;
uint32_t m_bit_buffer, m_bit_buffer_size;
uint64_t m_total_bits;
void append_byte(uint8_t c)
inline void append_byte(uint8_t c)
{
m_bytes.resize(m_bytes.size() + 1);
m_bytes.back() = c;
//m_bytes.resize(m_bytes.size() + 1);
//m_bytes.back() = c;
m_bytes.push_back(c);
}
static void end_nonzero_run(uint16_vec &syms, uint32_t &run_size, uint32_t len);
@@ -2672,6 +2781,31 @@ namespace basisu
*this = other;
}
image(image&& other) :
m_width(other.m_width), m_height(other.m_height), m_pitch(other.m_pitch),
m_pixels(std::move(other.m_pixels))
{
other.m_width = 0;
other.m_height = 0;
other.m_pitch = 0;
}
image& operator= (image&& rhs)
{
if (this != &rhs)
{
m_width = rhs.m_width;
m_height = rhs.m_height;
m_pitch = rhs.m_pitch;
m_pixels = std::move(rhs.m_pixels);
rhs.m_width = 0;
rhs.m_height = 0;
rhs.m_pitch = 0;
}
return *this;
}
image &swap(image &other)
{
std::swap(m_width, other.m_width);
@@ -2702,6 +2836,12 @@ namespace basisu
return *this;
}
image& match_dimensions(const image& other)
{
resize(other.get_width(), other.get_height());
return *this;
}
image &resize(uint32_t w, uint32_t h, uint32_t p = UINT32_MAX, const color_rgba& background = g_black_color)
{
return crop(w, h, p, background);
@@ -2913,7 +3053,7 @@ namespace basisu
const int sx = src_x + x;
if (sx < 0)
continue;
else if (sx >= (int)src.get_height())
else if (sx >= (int)src.get_width())
break;
set_clipped(dst_x + x, dst_y + y, src(sx, sy));
@@ -2955,6 +3095,8 @@ namespace basisu
return *this;
}
inline bool is_valid() const { return m_width > 0; }
inline uint32_t get_width() const { return m_width; }
inline uint32_t get_height() const { return m_height; }
inline uint32_t get_pitch() const { return m_pitch; }
@@ -3038,8 +3180,56 @@ namespace basisu
return *this;
}
void swap_rb()
{
for (auto& v : m_pixels)
std::swap(v.r, v.b);
}
void debug_text(uint32_t x_ofs, uint32_t y_ofs, uint32_t x_scale, uint32_t y_scale, const color_rgba &fg, const color_rgba *pBG, bool alpha_only, const char* p, ...);
vec4F get_filtered_vec4F(float x, float y) const
{
x -= .5f;
y -= .5f;
int ix = (int)floorf(x);
int iy = (int)floorf(y);
float wx = x - ix;
float wy = y - iy;
color_rgba a(get_clamped(ix, iy));
color_rgba b(get_clamped(ix + 1, iy));
color_rgba c(get_clamped(ix, iy + 1));
color_rgba d(get_clamped(ix + 1, iy + 1));
vec4F result;
for (uint32_t i = 0; i < 4; i++)
{
const float top = lerp<float>((float)a[i], (float)b[i], wx);
const float bot = lerp<float>((float)c[i], (float)d[i], wx);
const float m = lerp<float>((float)top, (float)bot, wy);
result[i] = m;
}
return result;
}
// (x,y) - Continuous coordinates, where pixel centers are at (.5,.5), valid image coords are [0,width] and [0,height]. Clamp addressing.
color_rgba get_filtered(float x, float y) const
{
const vec4F fresult(get_filtered_vec4F(x, y));
color_rgba result;
for (uint32_t i = 0; i < 4; i++)
result[i] = (uint8_t)clamp<int>((int)(fresult[i] + .5f), 0, 255);
return result;
}
private:
uint32_t m_width, m_height, m_pitch; // all in pixels
color_rgba_vec m_pixels;
@@ -3069,6 +3259,31 @@ namespace basisu
*this = other;
}
imagef(imagef&& other) :
m_width(other.m_width), m_height(other.m_height), m_pitch(other.m_pitch),
m_pixels(std::move(other.m_pixels))
{
other.m_width = 0;
other.m_height = 0;
other.m_pitch = 0;
}
imagef& operator= (imagef&& rhs)
{
if (this != &rhs)
{
m_width = rhs.m_width;
m_height = rhs.m_height;
m_pitch = rhs.m_pitch;
m_pixels = std::move(rhs.m_pixels);
rhs.m_width = 0;
rhs.m_height = 0;
rhs.m_pitch = 0;
}
return *this;
}
imagef &swap(imagef &other)
{
std::swap(m_width, other.m_width);
@@ -3118,6 +3333,12 @@ namespace basisu
return *this;
}
imagef& match_dimensions(const imagef& other)
{
resize(other.get_width(), other.get_height());
return *this;
}
imagef &resize(const imagef &other, uint32_t p = UINT32_MAX, const vec4F& background = vec4F(0,0,0,1))
{
return resize(other.get_width(), other.get_height(), p, background);
@@ -3248,7 +3469,7 @@ namespace basisu
const int sx = src_x + x;
if (sx < 0)
continue;
else if (sx >= (int)src.get_height())
else if (sx >= (int)src.get_width())
break;
set_clipped(dst_x + x, dst_y + y, src(sx, sy));
@@ -3274,10 +3495,12 @@ namespace basisu
return *this;
}
inline bool is_valid() const { return m_width > 0; }
inline uint32_t get_width() const { return m_width; }
inline uint32_t get_height() const { return m_height; }
inline uint32_t get_pitch() const { return m_pitch; }
inline uint32_t get_total_pixels() const { return m_width * m_height; }
inline uint64_t get_total_pixels() const { return (uint64_t)m_width * m_height; }
inline uint32_t get_block_width(uint32_t w) const { return (m_width + (w - 1)) / w; }
inline uint32_t get_block_height(uint32_t h) const { return (m_height + (h - 1)) / h; }
@@ -3315,7 +3538,7 @@ namespace basisu
{
if (!nan_msg)
{
fprintf(stderr, "One or more pixels was NaN, setting to 0.\n");
fprintf(stderr, "One or more input pixels was NaN, setting to 0.\n");
nan_msg = true;
}
}
@@ -3324,7 +3547,7 @@ namespace basisu
{
if (!inf_msg)
{
fprintf(stderr, "One or more pixels was INF, setting to 0.\n");
fprintf(stderr, "One or more input pixels was INF, setting to 0.\n");
inf_msg = true;
}
}
@@ -3333,7 +3556,7 @@ namespace basisu
{
if (!neg_zero_msg)
{
fprintf(stderr, "One or more pixels was -0, setting them to 0.\n");
fprintf(stderr, "One or more input pixels was -0, setting them to 0.\n");
neg_zero_msg = true;
}
}
@@ -3350,7 +3573,7 @@ namespace basisu
if (!neg_msg)
{
fprintf(stderr, "One or more pixels was negative -- setting these pixel components to 0 because ASTC HDR doesn't support signed values.\n");
fprintf(stderr, "One or more input pixels was negative -- setting these pixel components to 0 because ASTC HDR doesn't support signed values.\n");
neg_msg = true;
}
@@ -3363,7 +3586,7 @@ namespace basisu
if (!clamp_msg)
{
fprintf(stderr, "One or more pixels had to be clamped to %f.\n", highest_mag);
fprintf(stderr, "One or more input pixels had to be clamped to %f.\n", highest_mag);
clamp_msg = true;
}
@@ -3385,6 +3608,45 @@ namespace basisu
return *this;
}
bool has_alpha(uint32_t channel = 3) const
{
for (uint32_t y = 0; y < m_height; ++y)
for (uint32_t x = 0; x < m_width; ++x)
if ((*this)(x, y)[channel] != 1.0f)
return true;
return false;
}
vec4F get_filtered_vec4F(float x, float y) const
{
x -= .5f;
y -= .5f;
int ix = (int)floorf(x);
int iy = (int)floorf(y);
float wx = x - ix;
float wy = y - iy;
vec4F a(get_clamped(ix, iy));
vec4F b(get_clamped(ix + 1, iy));
vec4F c(get_clamped(ix, iy + 1));
vec4F d(get_clamped(ix + 1, iy + 1));
vec4F result;
for (uint32_t i = 0; i < 4; i++)
{
const float top = lerp<float>((float)a[i], (float)b[i], wx);
const float bot = lerp<float>((float)c[i], (float)d[i], wx);
const float m = lerp<float>((float)top, (float)bot, wy);
result[i] = m;
}
return result;
}
private:
uint32_t m_width, m_height, m_pitch; // all in pixels
@@ -3402,6 +3664,52 @@ namespace basisu
float linear_to_srgb(float l);
float srgb_to_linear(float s);
class fast_linear_to_srgb
{
public:
fast_linear_to_srgb()
{
init();
}
void init()
{
for (int i = 0; i < LINEAR_TO_SRGB_TABLE_SIZE; ++i)
{
float l = (float)i * (1.0f / (LINEAR_TO_SRGB_TABLE_SIZE - 1));
m_linear_to_srgb_table[i] = (uint8_t)basisu::fast_floorf_int(255.0f * basisu::linear_to_srgb(l));
}
float srgb_to_linear[256];
for (int i = 0; i < 256; i++)
srgb_to_linear[i] = basisu::srgb_to_linear((float)i / 255.0f);
for (int i = 0; i < 256; i++)
m_srgb_to_linear_thresh[i] = (srgb_to_linear[i] + srgb_to_linear[basisu::minimum<int>(i + 1, 255)]) * .5f;
}
inline uint8_t convert(float l) const
{
assert((l >= 0.0f) && (l <= 1.0f));
int j = basisu::fast_roundf_int((LINEAR_TO_SRGB_TABLE_SIZE - 1) * l);
assert((j >= 0) && (j < LINEAR_TO_SRGB_TABLE_SIZE));
int b = m_linear_to_srgb_table[j];
b += (l > m_srgb_to_linear_thresh[b]);
return (uint8_t)b;
}
private:
static constexpr int LINEAR_TO_SRGB_TABLE_SIZE = 2048;
uint8_t m_linear_to_srgb_table[LINEAR_TO_SRGB_TABLE_SIZE];
float m_srgb_to_linear_thresh[256];
};
extern fast_linear_to_srgb g_fast_linear_to_srgb;
// Image metrics
class image_metrics
@@ -3438,6 +3746,8 @@ namespace basisu
void calc(const image &a, const image &b, uint32_t first_chan = 0, uint32_t total_chans = 0, bool avg_comp_error = true, bool use_601_luma = false);
};
void print_image_metrics(const image& a, const image& b);
// Image saving/loading/resampling
bool load_png(const uint8_t* pBuf, size_t buf_size, image& img, const char* pFilename = nullptr);
@@ -3450,15 +3760,22 @@ namespace basisu
bool load_qoi(const char* pFilename, image& img);
bool load_jpg(const char *pFilename, image& img);
bool load_jpg(const uint8_t* pBuf, size_t buf_size, image& img);
inline bool load_jpg(const std::string &filename, image &img) { return load_jpg(filename.c_str(), img); }
// Currently loads .PNG, .TGA, or .JPG
bool load_image(const char* pFilename, image& img);
inline bool load_image(const std::string &filename, image &img) { return load_image(filename.c_str(), img); }
bool is_image_filename_hdr(const char* pFilename);
// Supports .HDR and most (but not all) .EXR's (see TinyEXR).
bool load_image_hdr(const char* pFilename, imagef& img, bool ldr_srgb_to_linear = true);
inline bool load_image_hdr(const std::string& filename, imagef& img, bool ldr_srgb_to_linear = true) { return load_image_hdr(filename.c_str(), img, ldr_srgb_to_linear); }
bool load_image_hdr(const char* pFilename, imagef& img, bool ldr_srgb_to_linear = true, float linear_nit_multiplier = 1.0f, float ldr_black_bias = 0.0f);
inline bool load_image_hdr(const std::string& filename, imagef& img, bool ldr_srgb_to_linear = true, float linear_nit_multiplier = 1.0f, float ldr_black_bias = 0.0f)
{
return load_image_hdr(filename.c_str(), img, ldr_srgb_to_linear, linear_nit_multiplier, ldr_black_bias);
}
enum class hdr_image_type
{
@@ -3466,10 +3783,11 @@ namespace basisu
cHITRGBAFloat = 1,
cHITPNGImage = 2,
cHITEXRImage = 3,
cHITHDRImage = 4
cHITHDRImage = 4,
cHITJPGImage = 5
};
bool load_image_hdr(const void* pMem, size_t mem_size, imagef& img, uint32_t width, uint32_t height, hdr_image_type img_type, bool ldr_srgb_to_linear);
bool load_image_hdr(const void* pMem, size_t mem_size, imagef& img, uint32_t width, uint32_t height, hdr_image_type img_type, bool ldr_srgb_to_linear, float linear_nit_multiplier = 1.0f, float ldr_black_bias = 0.0f);
uint8_t *read_tga(const uint8_t *pBuf, uint32_t buf_size, int &width, int &height, int &n_chans);
uint8_t *read_tga(const char *pFilename, int &width, int &height, int &n_chans);
@@ -3512,7 +3830,7 @@ namespace basisu
};
// Supports 1 (Y), 3 (RGB), or 4 (RGBA) channel images.
bool write_exr(const char* pFilename, imagef& img, uint32_t n_chans, uint32_t flags);
bool write_exr(const char* pFilename, const imagef& img, uint32_t n_chans, uint32_t flags);
enum
{
@@ -3572,102 +3890,6 @@ namespace basisu
inline double get_interval_timer() { return interval_timer::ticks_to_secs(interval_timer::get_ticks()); }
// 2D array
template<typename T>
class vector2D
{
typedef basisu::vector<T> TVec;
uint32_t m_width, m_height;
TVec m_values;
public:
vector2D() :
m_width(0),
m_height(0)
{
}
vector2D(uint32_t w, uint32_t h) :
m_width(0),
m_height(0)
{
resize(w, h);
}
vector2D(const vector2D &other)
{
*this = other;
}
vector2D &operator= (const vector2D &other)
{
if (this != &other)
{
m_width = other.m_width;
m_height = other.m_height;
m_values = other.m_values;
}
return *this;
}
inline bool operator== (const vector2D &rhs) const
{
return (m_width == rhs.m_width) && (m_height == rhs.m_height) && (m_values == rhs.m_values);
}
inline uint32_t size_in_bytes() const { return (uint32_t)m_values.size() * sizeof(m_values[0]); }
inline const T &operator() (uint32_t x, uint32_t y) const { assert(x < m_width && y < m_height); return m_values[x + y * m_width]; }
inline T &operator() (uint32_t x, uint32_t y) { assert(x < m_width && y < m_height); return m_values[x + y * m_width]; }
inline const T &operator[] (uint32_t i) const { return m_values[i]; }
inline T &operator[] (uint32_t i) { return m_values[i]; }
inline const T &at_clamped(int x, int y) const { return (*this)(clamp<int>(x, 0, m_width - 1), clamp<int>(y, 0, m_height - 1)); }
inline T &at_clamped(int x, int y) { return (*this)(clamp<int>(x, 0, m_width - 1), clamp<int>(y, 0, m_height - 1)); }
void clear()
{
m_width = 0;
m_height = 0;
m_values.clear();
}
void set_all(const T&val)
{
vector_set_all(m_values, val);
}
inline const T* get_ptr() const { return &m_values[0]; }
inline T* get_ptr() { return &m_values[0]; }
vector2D &resize(uint32_t new_width, uint32_t new_height)
{
if ((m_width == new_width) && (m_height == new_height))
return *this;
TVec oldVals(new_width * new_height);
oldVals.swap(m_values);
const uint32_t w = minimum(m_width, new_width);
const uint32_t h = minimum(m_height, new_height);
if ((w) && (h))
{
for (uint32_t y = 0; y < h; y++)
for (uint32_t x = 0; x < w; x++)
m_values[x + y * new_width] = oldVals[x + y * m_width];
}
m_width = new_width;
m_height = new_height;
return *this;
}
};
inline FILE *fopen_safe(const char *pFilename, const char *pMode)
{
#ifdef _WIN32
@@ -3723,12 +3945,14 @@ namespace basisu
};
typedef basisu::vector<pixel_block_hdr> pixel_block_hdr_vec;
void tonemap_image_reinhard(image& ldr_img, const imagef& hdr_img, float exposure);
void tonemap_image_reinhard(image& ldr_img, const imagef& hdr_img, float exposure, bool add_noise = false, bool per_component = true, bool luma_scaling = false);
bool tonemap_image_compressive(image& dst_img, const imagef& hdr_test_img);
bool tonemap_image_compressive2(image& dst_img, const imagef& hdr_test_img);
// Intersection
enum eClear { cClear = 0 };
enum eInitExpand { cInitExpand = 0 };
enum eIdentity { cIdentity = 0 };
template<typename vector_type>
class ray
@@ -3845,6 +4069,7 @@ namespace basisu
typedef vec_interval<vec3F> vec_interval3F;
typedef vec_interval<vec4F> vec_interval4F;
typedef vec_interval1F aabb1F;
typedef vec_interval2F aabb2F;
typedef vec_interval3F aabb3F;
@@ -4004,18 +4229,19 @@ namespace basisu
return result;
}
union fu32
{
uint32_t u;
float f;
};
// Supports positive and denormals only. No NaN or Inf.
inline float fast_half_to_float_pos_not_inf_or_nan(basist::half_float h)
BASISU_FORCE_INLINE float fast_half_to_float_pos_not_inf_or_nan(basist::half_float h)
{
assert(!basist::half_is_signed(h) && !basist::is_half_inf_or_nan(h));
union fu32
{
uint32_t u;
float f;
};
static const fu32 K = { 0x77800000 };
// add 112 to the exponent (112+half float's exp bias of 15=float32's bias of 127)
static const fu32 K = { 0x77800000 };
fu32 o;
o.u = h << 13;
@@ -4023,7 +4249,62 @@ namespace basisu
return o.f;
}
// Positive, negative, or denormals. No NaN or Inf. Clamped to MAX_HALF_FLOAT.
inline basist::half_float fast_float_to_half_trunc_no_nan_or_inf(float f)
{
assert(!isnan(f) && !isinf(f));
// Sutract 112 from the exponent, to change the bias from 127 to 15.
static const fu32 g_f_to_h{ 0x7800000 };
fu32 fu;
fu.f = minimum<float>((float)basist::MAX_HALF_FLOAT, fabsf(f)) * g_f_to_h.f;
return (basist::half_float)(((fu.u >> (23 - 10)) & 0x7FFF) | ((f < 0.0f) ? 0x8000 : 0));
}
inline basist::half_float fast_float_to_half_trunc_no_clamp_neg_nan_or_inf(float f)
{
assert(!isnan(f) && !isinf(f));
assert((f >= 0.0f) && (f <= basist::MAX_HALF_FLOAT));
// Sutract 112 from the exponent, to change the bias from 127 to 15.
static const fu32 g_f_to_h{ 0x7800000 };
fu32 fu;
fu.f = f * g_f_to_h.f;
return (basist::half_float)((fu.u >> (23 - 10)) & 0x7FFF);
}
inline basist::half_float fast_float_to_half_no_clamp_neg_nan_or_inf(float f)
{
assert(!isnan(f) && !isinf(f));
assert((f >= 0.0f) && (f <= basist::MAX_HALF_FLOAT));
// Sutract 112 from the exponent, to change the bias from 127 to 15.
static const fu32 g_f_to_h{ 0x7800000 };
fu32 fu;
fu.f = f * g_f_to_h.f;
uint32_t h = (basist::half_float)((fu.u >> (23 - 10)) & 0x7FFF);
// round to even or nearest
uint32_t mant = fu.u & 8191; // examine lowest 13 bits
uint32_t inc = (mant > 4096) | ((mant == 4096) & (h & 1));
h += inc;
if (h > basist::MAX_HALF_FLOAT_AS_INT_BITS)
h = basist::MAX_HALF_FLOAT_AS_INT_BITS;
return (basist::half_float)h;
}
} // namespace basisu
#include "basisu_math.h"
+16 -94
View File
@@ -353,9 +353,7 @@ namespace basisu
const uint32_t first_index = block_index_iter;
const uint32_t last_index = minimum<uint32_t>(m_total_blocks, first_index + N);
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->add_job([this, first_index, last_index] {
#endif
for (uint32_t block_index = first_index; block_index < last_index; block_index++)
{
@@ -388,15 +386,11 @@ namespace basisu
m_block_selector_cluster_index[block_index] = best_index;
}
#ifndef __EMSCRIPTEN__
});
#endif
}
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->wait_for_all();
#endif
m_encoded_blocks.resize(m_total_blocks);
for (uint32_t block_index = 0; block_index < m_total_blocks; block_index++)
@@ -425,9 +419,7 @@ namespace basisu
const uint32_t first_index = block_index_iter;
const uint32_t last_index = minimum<uint32_t>(m_total_blocks, first_index + N);
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->add_job([this, first_index, last_index, pass] {
#endif
for (uint32_t block_index = first_index; block_index < last_index; block_index++)
{
@@ -475,15 +467,11 @@ namespace basisu
} // block_index
#ifndef __EMSCRIPTEN__
});
#endif
}
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->wait_for_all();
#endif
m_endpoint_clusters.resize(0);
m_endpoint_clusters.resize(endpoints.size());
@@ -501,9 +489,7 @@ namespace basisu
const uint32_t first_index = block_index_iter;
const uint32_t last_index = minimum<uint32_t>(m_total_blocks, first_index + N);
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->add_job([this, first_index, last_index] {
#endif
for (uint32_t block_index = first_index; block_index < last_index; block_index++)
{
@@ -535,15 +521,11 @@ namespace basisu
m_block_selector_cluster_index[block_index] = best_index;
}
#ifndef __EMSCRIPTEN__
});
#endif
}
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->wait_for_all();
#endif
m_encoded_blocks.resize(m_total_blocks);
for (uint32_t block_index = 0; block_index < m_total_blocks; block_index++)
@@ -573,7 +555,7 @@ namespace basisu
debug_printf("introduce_special_selector_clusters\n");
uint32_t total_blocks_relocated = 0;
const uint32_t initial_selector_clusters = (uint32_t)m_selector_cluster_block_indices.size();
const uint32_t initial_selector_clusters = m_selector_cluster_block_indices.size_u32();
bool_vec block_relocated_flags(m_total_blocks);
@@ -595,7 +577,7 @@ namespace basisu
debug_printf("Introducing sel %u\n", sel);
const uint32_t new_selector_cluster_index = (uint32_t)m_optimized_cluster_selectors.size();
const uint32_t new_selector_cluster_index = m_optimized_cluster_selectors.size_u32();
m_optimized_cluster_selectors.push_back(blk);
@@ -675,7 +657,7 @@ namespace basisu
{
debug_printf("optimize_selector_codebook\n");
const uint32_t orig_total_selector_clusters = (uint32_t)m_optimized_cluster_selectors.size();
const uint32_t orig_total_selector_clusters = m_optimized_cluster_selectors.size_u32();
bool_vec selector_cluster_was_used(m_optimized_cluster_selectors.size());
for (uint32_t i = 0; i < m_total_blocks; i++)
@@ -787,9 +769,7 @@ namespace basisu
const uint32_t first_index = block_index_iter;
const uint32_t last_index = minimum<uint32_t>(m_total_blocks, first_index + N);
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->add_job([this, first_index, last_index] {
#endif
for (uint32_t block_index = first_index; block_index < last_index; block_index++)
{
@@ -830,15 +810,11 @@ namespace basisu
blk.set_selector(x, y, selectors[x + y * 4]);
}
#ifndef __EMSCRIPTEN__
});
#endif
}
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->wait_for_all();
#endif
} // use_cpu
@@ -859,9 +835,7 @@ namespace basisu
const uint32_t first_index = block_index_iter;
const uint32_t last_index = minimum<uint32_t>(m_total_blocks, first_index + N);
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->add_job( [this, first_index, last_index, &training_vecs] {
#endif
for (uint32_t block_index = first_index; block_index < last_index; block_index++)
{
@@ -883,15 +857,11 @@ namespace basisu
} // block_index;
#ifndef __EMSCRIPTEN__
} );
#endif
} // block_index_iter
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->wait_for_all();
#endif
}
void basisu_frontend::generate_endpoint_clusters()
@@ -970,7 +940,7 @@ namespace basisu
}
if (m_params.m_debug_stats)
debug_printf("Total endpoint clusters: %u, parent clusters: %u\n", (uint32_t)m_endpoint_clusters.size(), (uint32_t)m_endpoint_parent_clusters.size());
debug_printf("Total endpoint clusters: %u, parent clusters: %u\n", m_endpoint_clusters.size_u32(), m_endpoint_parent_clusters.size_u32());
}
// Iterate through each array of endpoint cluster block indices and set the m_block_endpoint_clusters_indices[][] array to indicaste which cluster index each block uses.
@@ -1040,11 +1010,9 @@ namespace basisu
for (uint32_t cluster_index_iter = 0; cluster_index_iter < m_endpoint_clusters.size(); cluster_index_iter += N)
{
const uint32_t first_index = cluster_index_iter;
const uint32_t last_index = minimum<uint32_t>((uint32_t)m_endpoint_clusters.size(), cluster_index_iter + N);
const uint32_t last_index = minimum<uint32_t>(m_endpoint_clusters.size_u32(), cluster_index_iter + N);
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->add_job( [this, first_index, last_index] {
#endif
for (uint32_t cluster_index = first_index; cluster_index < last_index; cluster_index++)
{
@@ -1112,15 +1080,11 @@ namespace basisu
}
} // cluster_index
#ifndef __EMSCRIPTEN__
} );
#endif
} // cluster_index_iter
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->wait_for_all();
#endif
vector_sort(m_subblock_endpoint_quant_err_vec);
}
@@ -1131,19 +1095,19 @@ namespace basisu
generate_block_endpoint_clusters();
int num_new_endpoint_clusters = m_params.m_max_endpoint_clusters - (uint32_t)m_endpoint_clusters.size();
int num_new_endpoint_clusters = m_params.m_max_endpoint_clusters - m_endpoint_clusters.size_u32();
if (num_new_endpoint_clusters <= 0)
return;
compute_endpoint_subblock_error_vec();
const uint32_t num_orig_endpoint_clusters = (uint32_t)m_endpoint_clusters.size();
const uint32_t num_orig_endpoint_clusters = m_endpoint_clusters.size_u32();
std::unordered_set<uint32_t> training_vector_was_relocated;
uint_vec cluster_sizes(num_orig_endpoint_clusters);
for (uint32_t i = 0; i < num_orig_endpoint_clusters; i++)
cluster_sizes[i] = (uint32_t)m_endpoint_clusters[i].size();
cluster_sizes[i] = m_endpoint_clusters[i].size_u32();
std::unordered_set<uint32_t> ignore_cluster;
@@ -1259,7 +1223,7 @@ namespace basisu
// TODO: Get this working when step>0
if (m_params.m_pOpenCL_context && !step)
{
const uint32_t total_clusters = m_endpoint_clusters.size();
const uint32_t total_clusters = (uint32_t)m_endpoint_clusters.size();
basisu::vector<cl_pixel_cluster> pixel_clusters(total_clusters);
@@ -1342,7 +1306,7 @@ namespace basisu
} // cluster_indices_iter
uint32_t* pSorted = radix_sort(colors.size(), colors.data(), colors2.data(), 0, 3);
uint32_t* pSorted = radix_sort((uint32_t)colors.size(), colors.data(), colors2.data(), 0, 3);
const uint64_t first_pixel_index = input_pixels.size();
@@ -1522,9 +1486,7 @@ namespace basisu
const uint32_t first_index = cluster_index_iter;
const uint32_t last_index = minimum<uint32_t>((uint32_t)m_endpoint_clusters.size(), cluster_index_iter + N);
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->add_job([this, first_index, last_index, step] {
#endif
for (uint32_t cluster_index = first_index; cluster_index < last_index; cluster_index++)
{
@@ -1643,15 +1605,11 @@ namespace basisu
} // cluster_index
#ifndef __EMSCRIPTEN__
});
#endif
} // cluster_index_iter
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->wait_for_all();
#endif
}
debug_printf("Elapsed time: %3.3f secs\n", tm.get_elapsed_secs());
@@ -1726,14 +1684,14 @@ namespace basisu
{
// For the OpenCL kernel, we order the parent endpoint clusters by smallest to largest for efficiency.
// We also prepare an array of block info structs that point into this new parent endpoint cluster array.
const uint32_t total_parent_clusters = m_endpoint_clusters_within_each_parent_cluster.size();
const uint32_t total_parent_clusters = (uint32_t)m_endpoint_clusters_within_each_parent_cluster.size();
basisu::vector<cl_block_info_struct> cl_block_info_structs(m_total_blocks);
// the size of each parent cluster, in total clusters
uint_vec parent_cluster_sizes(total_parent_clusters);
for (uint32_t i = 0; i < total_parent_clusters; i++)
parent_cluster_sizes[i] = m_endpoint_clusters_within_each_parent_cluster[i].size();
parent_cluster_sizes[i] = (uint32_t)m_endpoint_clusters_within_each_parent_cluster[i].size();
uint_vec first_parent_cluster_ofs(total_parent_clusters);
uint32_t cur_ofs = 0;
@@ -1818,9 +1776,7 @@ namespace basisu
const uint32_t first_index = block_index_iter;
const uint32_t last_index = minimum<uint32_t>(m_total_blocks, first_index + N);
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->add_job([this, first_index, last_index, &best_cluster_indices, &block_clusters] {
#endif
for (uint32_t block_index = first_index; block_index < last_index; block_index++)
{
@@ -1951,15 +1907,11 @@ namespace basisu
} // block_index
#ifndef __EMSCRIPTEN__
});
#endif
} // block_index_iter
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->wait_for_all();
#endif
} // use_cpu
@@ -2104,9 +2056,7 @@ namespace basisu
const uint32_t first_index = block_index_iter;
const uint32_t last_index = minimum<uint32_t>(m_total_blocks, first_index + N);
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->add_job([this, first_index, last_index] {
#endif
for (uint32_t block_index = first_index; block_index < last_index; block_index++)
{
@@ -2131,15 +2081,11 @@ namespace basisu
} // block_index
#ifndef __EMSCRIPTEN__
});
#endif
} // block_index_iter
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->wait_for_all();
#endif
} // use_cpu
@@ -2204,9 +2150,7 @@ namespace basisu
const uint32_t first_index = block_index_iter;
const uint32_t last_index = minimum<uint32_t>(m_total_blocks, first_index + N);
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->add_job( [this, first_index, last_index, &training_vecs] {
#endif
for (uint32_t block_index = first_index; block_index < last_index; block_index++)
{
@@ -2233,15 +2177,11 @@ namespace basisu
} // block_index
#ifndef __EMSCRIPTEN__
} );
#endif
} // block_index_iter
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->wait_for_all();
#endif
vec16F_clusterizer selector_clusterizer;
for (uint32_t i = 0; i < m_total_blocks; i++)
@@ -2335,9 +2275,7 @@ namespace basisu
const uint32_t first_index = cluster_index_iter;
const uint32_t last_index = minimum<uint32_t>((uint32_t)total_selector_clusters, cluster_index_iter + N);
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->add_job([this, first_index, last_index] {
#endif
for (uint32_t cluster_index = first_index; cluster_index < last_index; cluster_index++)
{
@@ -2406,15 +2344,11 @@ namespace basisu
} // cluster_index
#ifndef __EMSCRIPTEN__
});
#endif
} // cluster_index_iter
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->wait_for_all();
#endif
debug_printf("Elapsed time: %3.3f secs\n", tm.get_elapsed_secs());
@@ -2506,7 +2440,7 @@ namespace basisu
if ((m_params.m_pOpenCL_context) && m_use_hierarchical_selector_codebooks)
{
const uint32_t num_parent_clusters = m_selector_clusters_within_each_parent_cluster.size();
const uint32_t num_parent_clusters = m_selector_clusters_within_each_parent_cluster.size_u32();
basisu::vector<fosc_selector_struct> selector_structs;
selector_structs.reserve(m_optimized_cluster_selectors.size());
@@ -2534,7 +2468,7 @@ namespace basisu
selector_cluster_indices.push_back(selector_cluster_index);
}
cur_ofs += m_selector_clusters_within_each_parent_cluster[parent_index].size();
cur_ofs += m_selector_clusters_within_each_parent_cluster[parent_index].size_u32();
}
const uint32_t total_input_selectors = cur_ofs;
@@ -2549,7 +2483,7 @@ namespace basisu
block_structs[i].m_etc_color5_inten.a = (uint8_t)blk.get_inten_table(0);
block_structs[i].m_first_selector = parent_selector_cluster_offsets[parent_selector_cluster];
block_structs[i].m_num_selectors = m_selector_clusters_within_each_parent_cluster[parent_selector_cluster].size();
block_structs[i].m_num_selectors = m_selector_clusters_within_each_parent_cluster[parent_selector_cluster].size_u32();
}
uint_vec output_selector_cluster_indices(m_total_blocks);
@@ -2615,10 +2549,8 @@ namespace basisu
const uint32_t first_index = block_index_iter;
const uint32_t last_index = minimum<uint32_t>(m_total_blocks, first_index + N);
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->add_job( [this, first_index, last_index, &unpacked_optimized_cluster_selectors] {
#endif
int prev_best_cluster_index = 0;
for (uint32_t block_index = first_index; block_index < last_index; block_index++)
@@ -2756,15 +2688,11 @@ namespace basisu
} // block_index
#ifndef __EMSCRIPTEN__
} );
#endif
} // block_index_iter
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->wait_for_all();
#endif
for (uint32_t i = 0; i < m_selector_cluster_block_indices.size(); i++)
{
@@ -3081,9 +3009,7 @@ namespace basisu
const uint32_t first_index = cluster_index_iter;
const uint32_t last_index = minimum<uint32_t>((uint32_t)new_endpoint_cluster_block_indices.size(), cluster_index_iter + N);
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->add_job( [this, first_index, last_index, &cluster_improved, &cluster_valid, &new_endpoint_cluster_block_indices, &pBlock_selector_indices ] {
#endif
for (uint32_t cluster_index = first_index; cluster_index < last_index; cluster_index++)
{
@@ -3173,15 +3099,11 @@ namespace basisu
} // cluster_index
#ifndef __EMSCRIPTEN__
} );
#endif
} // cluster_index_iter
#ifndef __EMSCRIPTEN__
m_params.m_pJob_pool->wait_for_all();
#endif
uint32_t total_unused_clusters = 0;
uint32_t total_improved_clusters = 0;
+77 -8
View File
@@ -22,6 +22,8 @@
#define TINYDDS_IMPLEMENTATION
#include "3rdparty/tinydds.h"
#define BASISU_USE_GOOGLE_ASTC_DECODER (1)
namespace basisu
{
//------------------------------------------------------------------------------------------------
@@ -1424,6 +1426,7 @@ namespace basisu
case texture_format::cBC6HUnsigned:
case texture_format::cASTC_HDR_4x4:
case texture_format::cUASTC_HDR_4x4:
case texture_format::cASTC_HDR_6x6:
{
// Can't unpack HDR blocks in unpack_block() because it returns 32bpp pixel data.
assert(0);
@@ -1490,15 +1493,44 @@ namespace basisu
{
switch (fmt)
{
case texture_format::cASTC_HDR_6x6:
{
#if BASISU_USE_GOOGLE_ASTC_DECODER
bool status = basisu_astc::astc::decompress_hdr(&pPixels[0][0], (uint8_t*)pBlock, 6, 6);
assert(status);
if (!status)
return false;
#else
// Use our decoder
basist::half_float half_block[6 * 6][4];
astc_helpers::log_astc_block log_blk;
if (!astc_helpers::unpack_block(pBlock, log_blk, 6, 6))
return false;
if (!astc_helpers::decode_block(log_blk, half_block, 6, 6, astc_helpers::cDecodeModeHDR16))
return false;
for (uint32_t p = 0; p < (6 * 6); p++)
{
pPixels[p][0] = basist::half_to_float(half_block[p][0]);
pPixels[p][1] = basist::half_to_float(half_block[p][1]);
pPixels[p][2] = basist::half_to_float(half_block[p][2]);
pPixels[p][3] = basist::half_to_float(half_block[p][3]);
}
#endif
return true;
}
case texture_format::cASTC_HDR_4x4:
case texture_format::cUASTC_HDR_4x4:
{
#if 1
#if BASISU_USE_GOOGLE_ASTC_DECODER
// Use Google's decoder
bool status = basisu_astc::astc::decompress_hdr(&pPixels[0][0], (uint8_t*)pBlock, 4, 4);
assert(status);
if (!status)
return false;
#else
// Use our decoder
basist::half_float half_block[16][4];
astc_helpers::log_astc_block log_blk;
@@ -1595,10 +1627,8 @@ namespace basisu
bool gpu_image::unpack_hdr(imagef& img) const
{
if ((m_fmt != texture_format::cASTC_HDR_4x4) &&
(m_fmt != texture_format::cUASTC_HDR_4x4) &&
(m_fmt != texture_format::cBC6HUnsigned) &&
(m_fmt != texture_format::cBC6HSigned))
if ((m_fmt != texture_format::cASTC_HDR_4x4) && (m_fmt != texture_format::cUASTC_HDR_4x4) && (m_fmt != texture_format::cASTC_HDR_6x6) &&
(m_fmt != texture_format::cBC6HUnsigned) && (m_fmt != texture_format::cBC6HSigned))
{
// Can't call on LDR images, at least currently. (Could unpack the LDR data and convert to float.)
assert(0);
@@ -1646,6 +1676,7 @@ namespace basisu
KTX_RG = 0x8227,
KTX_RGB = 0x1907,
KTX_RGBA = 0x1908,
KTX_COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F0,
KTX_COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3,
KTX_COMPRESSED_RED_RGTC1_EXT = 0x8DBB,
@@ -1658,11 +1689,42 @@ namespace basisu
KTX_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT = 0x8E8F,
KTX_COMPRESSED_RGB_PVRTC_4BPPV1_IMG = 0x8C00,
KTX_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG = 0x8C02,
KTX_COMPRESSED_RGBA_ASTC_4x4_KHR = 0x93B0,
KTX_COMPRESSED_RGBA_ASTC_5x4_KHR = 0x93B1,
KTX_COMPRESSED_RGBA_ASTC_5x5_KHR = 0x93B2,
KTX_COMPRESSED_RGBA_ASTC_6x5_KHR = 0x93B3,
KTX_COMPRESSED_RGBA_ASTC_6x6_KHR = 0x93B4,
KTX_COMPRESSED_RGBA_ASTC_8x5_KHR = 0x93B5,
KTX_COMPRESSED_RGBA_ASTC_8x6_KHR = 0x93B6,
KTX_COMPRESSED_RGBA_ASTC_8x8_KHR = 0x93B7,
KTX_COMPRESSED_RGBA_ASTC_10x5_KHR = 0x93B8,
KTX_COMPRESSED_RGBA_ASTC_10x6_KHR = 0x93B9,
KTX_COMPRESSED_RGBA_ASTC_10x8_KHR = 0x93BA,
KTX_COMPRESSED_RGBA_ASTC_10x10_KHR = 0x93BB,
KTX_COMPRESSED_RGBA_ASTC_12x10_KHR = 0x93BC,
KTX_COMPRESSED_RGBA_ASTC_12x12_KHR = 0x93BD,
KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR = 0x93D0,
KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR = 0x93D1,
KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR = 0x93D2,
KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR = 0x93D3,
KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR = 0x93D4,
KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR = 0x93D5,
KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR = 0x93D6,
KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR = 0x93D7,
KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR = 0x93D8,
KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR = 0x93D9,
KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR = 0x93DA,
KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR = 0x93DB,
KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR = 0x93DC,
KTX_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR = 0x93DD,
KTX_COMPRESSED_RGBA_UASTC_4x4_KHR = 0x94CC, // TODO - Use proper value!
KTX_ATC_RGB_AMD = 0x8C92,
KTX_ATC_RGBA_INTERPOLATED_ALPHA_AMD = 0x87EE,
KTX_COMPRESSED_RGB_FXT1_3DFX = 0x86B0,
KTX_COMPRESSED_RGBA_FXT1_3DFX = 0x86B1,
KTX_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG = 0x9138,
@@ -1839,6 +1901,13 @@ namespace basisu
base_internal_fmt = KTX_RGBA;
break;
}
case texture_format::cASTC_HDR_6x6:
{
internal_fmt = KTX_COMPRESSED_RGBA_ASTC_6x6_KHR;
// TODO: should we write RGB? We don't support generating HDR 6x6 with alpha.
base_internal_fmt = KTX_RGBA;
break;
}
// We use different enums for HDR vs. LDR ASTC, but internally they are both just ASTC.
case texture_format::cASTC_LDR_4x4:
case texture_format::cASTC_HDR_4x4:
@@ -1995,11 +2064,11 @@ namespace basisu
assert(0);
return false;
}
slices = gpu_images.size() / 6;
slices = gpu_images.size_u32() / 6;
}
else
{
slices = gpu_images.size();
slices = gpu_images.size_u32();
}
uint32_t width = 0, height = 0, total_levels = 0;
@@ -2117,7 +2186,7 @@ namespace basisu
assert(total_levels < 32);
for (uint32_t i = 0; i < total_levels; i++)
{
mipmap_sizes[i] = mipmaps[i].size_in_bytes();
mipmap_sizes[i] = mipmaps[i].size_in_bytes_u32();
mipmap_ptrs[i] = mipmaps[i].get_ptr();
}
+15 -7
View File
@@ -789,7 +789,7 @@ namespace basisu
struct opencl_context
{
uint32_t m_ocl_total_pixel_blocks;
size_t m_ocl_total_pixel_blocks;
cl_mem m_ocl_pixel_blocks;
cl_command_queue m_command_queue;
@@ -907,7 +907,7 @@ namespace basisu
};
#pragma pack(pop)
bool opencl_set_pixel_blocks(opencl_context_ptr pContext, uint32_t total_blocks, const cl_pixel_block* pPixel_blocks)
bool opencl_set_pixel_blocks(opencl_context_ptr pContext, size_t total_blocks, const cl_pixel_block* pPixel_blocks)
{
if (!opencl_is_available())
return false;
@@ -938,9 +938,11 @@ namespace basisu
assert(pContext->m_ocl_pixel_blocks);
if (!pContext->m_ocl_pixel_blocks)
return false;
assert(pContext->m_ocl_total_pixel_blocks <= INT_MAX);
cl_encode_etc1s_param_struct ps;
ps.m_total_blocks = pContext->m_ocl_total_pixel_blocks;
ps.m_total_blocks = (int)pContext->m_ocl_total_pixel_blocks;
ps.m_perceptual = perceptual;
ps.m_total_perms = total_perms;
@@ -1062,9 +1064,11 @@ exit:
assert(pContext->m_ocl_pixel_blocks);
if (!pContext->m_ocl_pixel_blocks)
return false;
assert(pContext->m_ocl_total_pixel_blocks <= INT_MAX);
cl_rec_param_struct ps;
ps.m_total_blocks = pContext->m_ocl_total_pixel_blocks;
ps.m_total_blocks = (int)pContext->m_ocl_total_pixel_blocks;
ps.m_perceptual = perceptual;
bool status = false;
@@ -1118,8 +1122,10 @@ exit:
if (!pContext->m_ocl_pixel_blocks)
return false;
assert(pContext->m_ocl_total_pixel_blocks <= INT_MAX);
fosc_param_struct ps;
ps.m_total_blocks = pContext->m_ocl_total_pixel_blocks;
ps.m_total_blocks = (int)pContext->m_ocl_total_pixel_blocks;
ps.m_perceptual = perceptual;
bool status = false;
@@ -1170,8 +1176,10 @@ exit:
if (!pContext->m_ocl_pixel_blocks)
return false;
assert(pContext->m_ocl_total_pixel_blocks <= INT_MAX);
ds_param_struct ps;
ps.m_total_blocks = pContext->m_ocl_total_pixel_blocks;
ps.m_total_blocks = (int)pContext->m_ocl_total_pixel_blocks;
ps.m_perceptual = perceptual;
bool status = false;
@@ -1232,7 +1240,7 @@ namespace basisu
BASISU_NOTE_UNUSED(context);
}
bool opencl_set_pixel_blocks(opencl_context_ptr pContext, uint32_t total_blocks, const cl_pixel_block* pPixel_blocks)
bool opencl_set_pixel_blocks(opencl_context_ptr pContext, size_t total_blocks, const cl_pixel_block* pPixel_blocks)
{
BASISU_NOTE_UNUSED(pContext);
BASISU_NOTE_UNUSED(total_blocks);
+1 -1
View File
@@ -43,7 +43,7 @@ namespace basisu
// Must match BASISU_ETC1_CLUSTER_FIT_ORDER_TABLE_SIZE
const uint32_t OPENCL_ENCODE_ETC1S_MAX_PERMS = 165;
bool opencl_set_pixel_blocks(opencl_context_ptr pContext, uint32_t total_blocks, const cl_pixel_block* pPixel_blocks);
bool opencl_set_pixel_blocks(opencl_context_ptr pContext, size_t total_blocks, const cl_pixel_block* pPixel_blocks);
bool opencl_encode_etc1s_blocks(opencl_context_ptr pContext, etc_block* pOutput_blocks, bool perceptual, uint32_t total_perms);
+11 -15
View File
@@ -20,8 +20,7 @@
namespace basisu
{
#define BOX_FILTER_SUPPORT (0.5f)
static float box_filter(float t) /* pulse/Fourier window */
float box_filter(float t) /* pulse/Fourier window */
{
// make_clist() calls the filter function with t inverted (pos = left, neg = right)
if ((t >= -0.5f) && (t < 0.5f))
@@ -29,9 +28,8 @@ namespace basisu
else
return 0.0f;
}
#define TENT_FILTER_SUPPORT (1.0f)
static float tent_filter(float t) /* box (*) box, bilinear/triangle */
float tent_filter(float t) /* box (*) box, bilinear/triangle */
{
if (t < 0.0f)
t = -t;
@@ -42,8 +40,7 @@ namespace basisu
return 0.0f;
}
#define BELL_SUPPORT (1.5f)
static float bell_filter(float t) /* box (*) box (*) box */
float bell_filter(float t) /* box (*) box (*) box */
{
if (t < 0.0f)
t = -t;
@@ -201,13 +198,12 @@ namespace basisu
return (0.0f);
}
#define GAUSSIAN_SUPPORT (1.25f)
static float gaussian_filter(float t) // with blackman window
float gaussian_filter(float t) // with blackman window
{
if (t < 0)
t = -t;
if (t < GAUSSIAN_SUPPORT)
return clean(exp(-2.0f * t * t) * sqrt(2.0f / M_PI) * blackman_exact_window(t / GAUSSIAN_SUPPORT));
if (t < BASISU_GAUSSIAN_FILTER_SUPPORT)
return clean(exp(-2.0f * t * t) * sqrt(2.0f / M_PI) * blackman_exact_window(t / BASISU_GAUSSIAN_FILTER_SUPPORT));
else
return 0.0f;
}
@@ -310,9 +306,9 @@ namespace basisu
const resample_filter g_resample_filters[] =
{
{ "box", box_filter, BOX_FILTER_SUPPORT },
{ "tent", tent_filter, TENT_FILTER_SUPPORT },
{ "bell", bell_filter, BELL_SUPPORT },
{ "box", box_filter, BASISU_BOX_FILTER_SUPPORT },
{ "tent", tent_filter, BASISU_TENT_FILTER_SUPPORT },
{ "bell", bell_filter, BASISU_BELL_FILTER_SUPPORT },
{ "b-spline", B_spline_filter, B_SPLINE_SUPPORT },
{ "mitchell", mitchell_filter, MITCHELL_SUPPORT },
{ "blackman", blackman_filter, BLACKMAN_SUPPORT },
@@ -321,7 +317,7 @@ namespace basisu
{ "lanczos6", lanczos6_filter, LANCZOS6_SUPPORT },
{ "lanczos12", lanczos12_filter, LANCZOS12_SUPPORT },
{ "kaiser", kaiser_filter, KAISER_SUPPORT },
{ "gaussian", gaussian_filter, GAUSSIAN_SUPPORT },
{ "gaussian", gaussian_filter, BASISU_GAUSSIAN_FILTER_SUPPORT },
{ "catmullrom", catmull_rom_filter, CATMULL_ROM_SUPPORT },
{ "quadratic_interp", quadratic_interp_filter, QUADRATIC_SUPPORT },
{ "quadratic_approx", quadratic_approx_filter, QUADRATIC_SUPPORT },
+1 -1
View File
@@ -573,7 +573,7 @@ namespace basisu
/* Don't deallocate a contibutor list
* if the user passed us one of their own.
*/
*/
if ((m_Pclist_x) && (!m_clist_x_forced))
{
+2
View File
@@ -113,6 +113,8 @@ namespace basisu
Resample_Real filter_scale,
Resample_Real src_ofs);
static void free_clist(Contrib_List* p) { if (p) { free(p->p); free(p); } }
private:
Resampler();
Resampler(const Resampler &o);
+12
View File
@@ -29,6 +29,18 @@ namespace basisu
extern const resample_filter g_resample_filters[];
extern const int g_num_resample_filters;
const float BASISU_BOX_FILTER_SUPPORT = 0.5f;
float box_filter(float t); /* pulse/Fourier window */
const float BASISU_TENT_FILTER_SUPPORT = 1.0f;
float tent_filter(float t); /* box (*) box, bilinear/triangle */
const float BASISU_GAUSSIAN_FILTER_SUPPORT = 1.25f;
float gaussian_filter(float t); // with blackman window
const float BASISU_BELL_FILTER_SUPPORT = 1.5f;
float bell_filter(float t); /* box (*) box (*) box */
int find_resample_filter(const char *pName);
+2
View File
@@ -91,6 +91,8 @@ namespace basisu
void gaussian_filter(imagef &dst, const imagef &orig_img, uint32_t odd_filter_width, float sigma_sqr, bool wrapping, uint32_t width_divisor, uint32_t height_divisor)
{
assert(&dst != &orig_img);
assert(odd_filter_width && (odd_filter_width & 1));
odd_filter_width |= 1;
+1 -32
View File
@@ -3813,32 +3813,7 @@ namespace basisu
return hash_hsieh((const uint8_t*)&s, sizeof(s));
}
};
class tracked_stat
{
public:
tracked_stat() { clear(); }
void clear() { m_num = 0; m_total = 0; m_total2 = 0; }
void update(uint32_t val) { m_num++; m_total += val; m_total2 += val * val; }
tracked_stat& operator += (uint32_t val) { update(val); return *this; }
uint32_t get_number_of_values() { return m_num; }
uint64_t get_total() const { return m_total; }
uint64_t get_total2() const { return m_total2; }
float get_average() const { return m_num ? (float)m_total / m_num : 0.0f; };
float get_std_dev() const { return m_num ? sqrtf((float)(m_num * m_total2 - m_total * m_total)) / m_num : 0.0f; }
float get_variance() const { float s = get_std_dev(); return s * s; }
private:
uint32_t m_num;
uint64_t m_total;
uint64_t m_total2;
};
static bool uastc_rdo_blocks(uint32_t first_index, uint32_t last_index, basist::uastc_block* pBlocks, const color_rgba* pBlock_pixels, const uastc_rdo_params& params, uint32_t flags,
uint32_t &total_skipped, uint32_t &total_refined, uint32_t &total_modified, uint32_t &total_smooth)
{
@@ -4150,9 +4125,7 @@ namespace basisu
const uint32_t first_index = block_index_iter;
const uint32_t last_index = minimum<uint32_t>(num_blocks, block_index_iter + blocks_per_job);
#ifndef __EMSCRIPTEN__
pJob_pool->add_job([first_index, last_index, pBlocks, pBlock_pixels, &params, flags, &total_skipped, &total_modified, &total_refined, &total_smooth, &all_succeeded, &stat_mutex] {
#endif
uint32_t job_skipped = 0, job_modified = 0, job_refined = 0, job_smooth = 0;
@@ -4168,16 +4141,12 @@ namespace basisu
total_smooth += job_smooth;
}
#ifndef __EMSCRIPTEN__
}
);
#endif
} // block_index_iter
#ifndef __EMSCRIPTEN__
pJob_pool->wait_for_all();
#endif
status = all_succeeded;
}