Pure-C-API-Compression

This content was automatically converted from the project's wiki Markdown to HTML. See the Basis Universal GitHub wiki for the latest content.

This documentation is based off Basis Universal v2.10.

Table of Contents

Intro

See Header: basisu_wasm_api.h

This header provides the C-style encoder/compressor API for Basis Universal. It supports compressing RGBA32 or float RGBA images into .basis or .ktx2 files using ETC1S, UASTC LDR/HDR, XUASTC LDR, or ASTC LDR/HDR formats.

All common constants, types, and flags referenced below are defined in Pure C API - Common Definitions page (or see basisu_wasm_api_common.h).

These API's are essentially C-style wrappers to the C++ API's basis_compress2() compression helper functions, defined in encoder/basisu_comp.h. Also see the remarks before this function.

Also see KTX2 File Format Support Technical Details, which has low level information about each of the supported codecs and how they are written to KTX2 files.

Note: These API's aren't just for WASM WASI module usage, they are also available for calling the native library via plain C API's.


Initialization

bu_get_version()

uint32_t bu_get_version();

Returns the Basis Universal library version number (decimal 200 for library version v2.00).

bu_enable_debug_printf()

void bu_enable_debug_printf(uint32_t flag);

Enables or disables internal debug printf output globally. To see any debug output, this function MUST be called, then use the BU_COMP_FLAGS_DEBUG_OUTPUT flag during compression.

Parameter Type Description
flag uint32_t Non-zero to enable, 0 to disable.

bu_init()

void bu_init();

Initializes the Basis Universal encoder. Must be called once, and allowed to finish and fully return, before any other bu_* functions are called.

Internally this calls basisu_encoder_init(), which is mutexed.


Memory Management

Use bu_alloc and bu_free to manage heap allocated buffers that you pass to the API. In WASI modules, the returned uint64_t's are casted pointers into the module's internal address space. In WASM mode, all heap buffers you pass to the C API must be allocated via these API's. In native (non-WASM) builds, these "offsets" are just pointers casted to uint64_t values.

bu_alloc()

uint64_t bu_alloc(uint64_t size);

Allocates a block of memory (alias to the module's malloc() function).

Parameter Type Description
size uint64_t Number of bytes to allocate.

Returns: Module offset (address) of the allocated block, or 0 on failure.

bu_free()

void bu_free(uint64_t ofs);

Frees a block of memory previously allocated by bu_alloc. Alias to the module's free() function.

Parameter Type Description
ofs uint64_t Offset returned by a prior bu_alloc call.

Compression Parameters

Compression parameters are managed through an opaque handle (a uint64_t offset). Create a parameter block, configure it with source images, then pass it to bu_compress_texture().

bu_new_comp_params()

uint64_t bu_new_comp_params();

Allocates and returns a new compression parameter block with default settings.

Returns: Offset of the new parameter block, or 0 on failure.

bu_delete_comp_params()

wasm_bool_t bu_delete_comp_params(uint64_t params_ofs);

Frees a compression parameter block.

Parameter Type Description
params_ofs uint64_t Offset of the parameter block to free.

Returns: Non-zero on success, 0 on failure.

bu_comp_params_clear()

wasm_bool_t bu_comp_params_clear(uint64_t params_ofs);

Resets a compression parameter block to its default state, allowing it to be reused for a new compression without reallocating.

Parameter Type Description
params_ofs uint64_t Offset of the parameter block to clear.

Returns: Non-zero on success, 0 on failure.


Retrieving Compressed Output

After a successful call to bu_compress_texture(), the compressed data is stored inside the parameter block. Use these functions to retrieve it.

bu_comp_params_get_comp_data_ofs()

uint64_t bu_comp_params_get_comp_data_ofs(uint64_t params_ofs);

Returns the memory offset of the compressed output data.

Parameter Type Description
params_ofs uint64_t Offset of the parameter block.

Returns: Offset of the compressed .basis or .ktx2 file data in memory.

bu_comp_params_get_comp_data_size()

uint64_t bu_comp_params_get_comp_data_size(uint64_t params_ofs);

Returns the size in bytes of the compressed output data.

Parameter Type Description
params_ofs uint64_t Offset of the parameter block.

Returns: Size of the compressed data in bytes.


Setting Source Images

Source images are set on the parameter block before compression. Each image is identified by an image_index (starting at 0). The pixel data must already be allocated in linear memory via bu_alloc. The compressor immediately copies the image data, so it can be freed after either set function is called.

For the LDR codecs, the bu_comp_params_set_image_rgba32() API must be used, and for the HDR codecs bu_comp_params_set_image_float_rgba() must be used. Mixing LDR and HDR images isn't supported and will cause compression to fail. The current C API does not support automatic LDR/SDR->HDR upconversion.

For BU_COMP_FLAGS_TEXTURE_TYPE_2D (the default), one source image can be provided per mipmap level, largest mipmap level first. If a single image is provided, mipmaps can be generated automatically by using the BU_COMP_FLAGS_GEN_MIPS_CLAMP or BU_COMP_FLAGS_GEN_MIPS_WRAP flags.

For the other texture types (BU_COMP_FLAGS_TEXTURE_TYPE_2D_ARRAY, BU_COMP_FLAGS_TEXTURE_TYPE_CUBEMAP_ARRAY, or BU_COMP_FLAGS_TEXTURE_TYPE_VIDEO_FRAMES), the source images are cubemap faces, texture array layers, or texture video frames. The current C API doesn't support also specifying user-specified mipmaps for these texture types (however the full C++ API does), but it does support automatic mipmap generation.

bu_comp_params_set_image_rgba32()

wasm_bool_t bu_comp_params_set_image_rgba32(
    uint64_t params_ofs,
    uint32_t image_index,
    uint64_t img_data_ofs,
    uint32_t width, uint32_t height,
    uint32_t pitch_in_bytes);

Sets an 8-bit-per-channel RGBA source image (4 bytes per pixel).

Parameter Type Description
params_ofs uint64_t Offset of the parameter block.
image_index uint32_t Zero-based index of the image to set.
img_data_ofs uint64_t Offset of the pixel data in memory (allocated via bu_alloc).
width uint32_t Image width in pixels.
height uint32_t Image height in pixels.
pitch_in_bytes uint32_t Row stride in bytes. Use width * 4 for tightly packed rows.

Returns: Non-zero on success, 0 on failure.

bu_comp_params_set_image_float_rgba()

wasm_bool_t bu_comp_params_set_image_float_rgba(
    uint64_t params_ofs,
    uint32_t image_index,
    uint64_t img_data_ofs,
    uint32_t width, uint32_t height,
    uint32_t pitch_in_bytes);

Sets a 32-bit-float-per-channel RGBA source image (16 bytes per pixel). Use this for HDR content. See the Valid HDR Inputs section in the C++ API documentation for information about the valid range of HDR input values.

Parameter Type Description
params_ofs uint64_t Offset of the parameter block.
image_index uint32_t Zero-based index of the image to set.
img_data_ofs uint64_t Offset of the pixel data in memory (allocated via bu_alloc).
width uint32_t Image width in pixels.
height uint32_t Image height in pixels.
pitch_in_bytes uint32_t Row stride in bytes. Use width * 16 for tightly packed rows.

Returns: Non-zero on success, 0 on failure.


Compression

bu_compress_texture()

wasm_bool_t bu_compress_texture(
    uint64_t params_ofs,
    uint32_t desired_basis_tex_format,
    int      quality_level,
    int      effort_level,
    uint64_t flags_and_quality,
    float    low_level_uastc_rdo_or_dct_quality);

Compresses the source image(s) previously set on the parameter block. The parameter block must contain a full mipmap chain, or a single image. This function is a wrapper over basis_compress2() in the C++ library.

Parameter Type Description
params_ofs uint64_t Offset of the parameter block (with source images already set).
desired_basis_tex_format uint32_t Target file format — one of the BTF_* constants (e.g. BTF_ETC1S, BTF_UASTC_LDR_4X4).
quality_level int Unified Quality level [1,100] (see BU_QUALITY_MIN, BU_QUALITY_MAX). Use -1 to use older non-unified/direct codec-specific quality level or lambda (low 8-bits of flags_and_quality, or via low_level_uastc_rdo_or_dct_quality).
effort_level int Unified Encoder effort [0,10] (see BU_EFFORT_MIN, BU_EFFORT_MAX). See BU_EFFORT_* presets. Use -1 to use older non-unified/direct codec-specific effort level (low 8-bits of flags_and_quality for some codecs).
flags_and_quality uint64_t Bitwise OR of BU_COMP_FLAGS_* constants. Controls output format, mipmaps, color space, etc. Low 8-bits are either the older non-unified quality level, or for some codecs the non-unified effort level.
low_level_uastc_rdo_or_dct_quality float Low-level (non-unified) quality or lambda parameter for UASTC RDO encoding. Typically 0.0 for defaults. Must be 0.0 if using unified (not -1) quality level.

Returns: Non-zero on success, 0 on failure.

After a successful return, retrieve the output file data using bu_comp_params_get_comp_data_ofs() and bu_comp_params_get_comp_data_size().

This function is thread-safe, i.e. you can compress multiple textures in parallel on different threads.


Typical Usage

See the C API Example.

High level C example:

#define TRUE (1)
#define FALSE (0)

// Initialize library (once at startup)
bu_init();

// Globally enable library debug output
bu_enable_debug_printf(TRUE);

// Create compression parameters handle
uint64_t params = bu_new_comp_params();

// 32bpp RGBA image's width/height (created elsewhere)
int width, height;
const void *source_image_pixels;

// Allocate and set source image (assume you have RGBA data)
uint64_t img_ofs = bu_alloc(width * height * 4);

memcpy((void*)img_ofs, source_image_pixels, width * height * 4);

// Compressor copies data internally
bu_comp_params_set_image_rgba32(params, 0, img_ofs, width, height, width * 4);

// Free allocation
bu_free(img_ofs); 
img_ofs = 0;

// Compress to XUASTC LDR 6x6 with quality 75, effort 3
uint32_t flags = BU_COMP_FLAGS_KTX2_OUTPUT | BU_COMP_FLAGS_KTX2_UASTC_ZSTD | BU_COMP_FLAGS_SRGB | BU_COMP_FLAGS_THREADED | BU_COMP_FLAGS_DEBUG_OUTPUT;
if (!bu_compress_texture(params, BTF_XUASTC_LDR_6X6, 75, 3, flags, 0.0f)) 
{
    // Cleanup
    bu_delete_comp_params(params);

    // Handle error
}

// Retrieve compressed output
uint64_t size = bu_comp_params_get_comp_data_size(params);
if (!size)
{
    // Handle error   
}

void* data = (void*)bu_comp_params_get_comp_data_ofs(params);
if (!data)
{
    // Handle error   
}

// ... write file data to file or use as needed ...

// Cleanup
bu_delete_comp_params(params);

API Usage Details

The developer can use either the unified, codec-independent quality/effort levels (recommended for simplicity), or lower level codec-specific parameters. It's possible to mix unified quality with a non-unified effort setting, or vice versa, but it's not recommended.

Codec unified effort/quality level support matrix:

Codec Quality Control Effort Control Standard ASTC in .KTX2 File Built-in Compression/RDO+Zstd Support Rough On-Disk Bitrate Range
ETC1S ✅ Unified ✅ Unified (Only) No Built-in: VQ+entropy coding of ETC1S latent, custom format ~0.5 - ~2.5 bpp
UASTC LDR 4×4 ✅ Unified ✅ Unified No Via RDO lambda+Zstd ~4 - 8.0 bpp
UASTC HDR 4×4 ❌ Ignored ✅ Unified Yes No (non-RDO+Zstd) ~6 - 8.0 bpp
ASTC HDR 6×6 ✅ Unified ✅ Unified Yes Via RDO lambda+Zstd ~2.25 - 3.56 bpp
UASTC HDR 6×6i ✅ Unified ✅ Unified No Built-in: latent compression of ASTC, custom format ~1.75 - ~3.0 bpp
ASTC LDR 4×4-12×12 ❌ Ignored ✅ Unified Yes No (non-RDO+Zstd, Windowed RDO not yet exposed via C API) ~0.89 - 8.0 bpp
XUASTC LDR 4×4-12×12 ✅ Unified ✅ Unified No Built-in: latent compression+DCT+entropy coding of ASTC, custom format ~0.3 - ~5.7 bpp

These codecs support direct quality control or RDO:

Note Zstd is only available when writing .KTX2 target files, and is only applied when the BU_COMP_FLAGS_KTX2_UASTC_ZSTD flag (which enables Zstd supercompression) is specified during compression. For codecs with their own built-in compression (like ETC1S or XUASTC), the BU_COMP_FLAGS_KTX2_UASTC_ZSTD flag is harmless and will be ignored.

These codecs do not support RDO (at least not via the C API), and the quality level will be ignored:

If you specify a quality level less than 100 for a codec which doesn't support quality levels, a warning message will be printed to stdout.

For the ETC1S codec: The effort level can only be controlled via the C API when using the unified effort_level parameter.

For the HDR codecs, the input image(s) MUST be float RGBA HDR: use bu_comp_params_set_image_float_rgba() to provide the input image(s). Automatic SDR/LDR->HDR upconversion isn't supported yet via the pure C API.

Example Parameters

bu_compress_texture(params_ofs, 
    BTF_ETC1S, // codec selection
    75, 2,  // quality 75, effort 2
    BU_COMP_FLAGS_THREADED | BU_COMP_FLAGS_SRGB | BU_COMP_FLAGS_KTX2_OUTPUT | BU_COMP_FLAGS_KTX2_UASTC_ZSTD, // flags: threaded, sRGB, KTX2
    0.0f);
bu_compress_texture(params_ofs, 
    BTF_UASTC_LDR_4X4, // codec selection
    75, 2,  // quality 75 (100=no RDO), effort 2
    BU_COMP_FLAGS_THREADED | BU_COMP_FLAGS_SRGB | BU_COMP_FLAGS_KTX2_OUTPUT | BU_COMP_FLAGS_KTX2_UASTC_ZSTD, // flags: threaded, sRGB, KTX2
    0.0f);

No support for quality levels/RDO.

bu_compress_texture(params_ofs, 
    BTF_UASTC_HDR_4X4, // codec selection
    100, 2,  // quality 100 (ignored), effort 2
    BU_COMP_FLAGS_THREADED | BU_COMP_FLAGS_KTX2_OUTPUT | BU_COMP_FLAGS_KTX2_UASTC_ZSTD, // flags: threaded, KTX2
    0.0f);

Internally the same codec is used for ASTC HDR 6x6 and UASTC HDR 6x6i.

bu_compress_texture(params_ofs, 
    BTF_ASTC_HDR_6X6, // codec selection
    85, 2,  // quality 85 (100=no RDO), effort 2
    BU_COMP_FLAGS_THREADED | BU_COMP_FLAGS_KTX2_OUTPUT | BU_COMP_FLAGS_KTX2_UASTC_ZSTD, // flags: threaded, KTX2
    0.0f);

Internally the same codec is used for ASTC HDR 6x6 and UASTC HDR 6x6i.

bu_compress_texture(params_ofs, 
    BTF_UASTC_HDR_6X6, // codec selection
    95, 2,  // quality 95 (100=no RDO), effort 2
    BU_COMP_FLAGS_THREADED | BU_COMP_FLAGS_KTX2_OUTPUT | BU_COMP_FLAGS_KTX2_UASTC_ZSTD, // flags: threaded, KTX2
    0.0f);

Internally the same codec is used for ASTC LDR 4x4-12x12 and XUASTC LDR 4x4-12x12.

No support for quality levels/RDO via the C API.

bu_compress_texture(params_ofs, 
    BTF_ASTC_LDR_4X4, // codec selection
    100, 3,  // quality 100 (ignored), effort 3
    BU_COMP_FLAGS_THREADED | BU_COMP_FLAGS_SRGB | BU_COMP_FLAGS_KTX2_OUTPUT | BU_COMP_FLAGS_KTX2_UASTC_ZSTD, // flags: threaded, KTX2, sRGB
    0.0f);

Internally the same codec is used for ASTC LDR 4x4-12x12 and XUASTC LDR 4x4-12x12.

bu_compress_texture(params_ofs, 
    BTF_XUASTC_LDR_4X4, // codec selection
    75, 3,  // quality 75 (100=no DCT), effort 3
    BU_COMP_FLAGS_THREADED | BU_COMP_FLAGS_SRGB | BU_COMP_FLAGS_KTX2_OUTPUT | BU_COMP_FLAGS_KTX2_UASTC_ZSTD, // flags: threaded, KTX2, sRGB
    0.0f);

Using the older/original non-unified (i.e. codec-specific) options, for lower level control:

Beware: This older method for controlling codec quality and effort has way sharper edges, i.e. it's easy to set the wrong parameters because the quality and effort level are highly codec-specific. We recommend enabling codec debug output (call bu_enable_debug_printf() and enable the BU_COMP_FLAGS_DEBUG_OUTPUTflag) to double check you've set the parameters as you expect.

This method does enable direct control over RDO lambda for the HDR codecs, which can be quite useful. It's challenging for us to map a unified [1,100] quality range to a useable lambda setting that works well across a diverse range of HDR imagery.

Only the quality level is controllable (via this API using non-unified effort levels), not the effort level.

uint32_t flags = BU_COMP_FLAGS_KTX2_OUTPUT | BU_COMP_FLAGS_KTX2_UASTC_ZSTD | BU_COMP_FLAGS_SRGB | BU_COMP_FLAGS_THREADED;
int old_quality_effort = 200; // select ETC1S quality level 200, valid range [1,255]
bu_compress_texture(comp_params, BTF_ETC1S, -1, -1, flags | old_quality_effort, 0.0f))
uint32_t flags = BU_COMP_FLAGS_KTX2_OUTPUT | BU_COMP_FLAGS_KTX2_UASTC_ZSTD | BU_COMP_FLAGS_SRGB | BU_COMP_FLAGS_THREADED;
int old_quality_effort = 2; // select UASTC LDR 4x4 with default effort level 2 (cPackUASTCLevelDefault), valid range [0,4]
float rdo_lambda = 1.0f; // RDO lambda, 0=no RDO, higher=lower quality, try [0.1-10.0]
bu_compress_texture(comp_params, BTF_UASTC_LDR_4X4, -1, -1, flags | old_quality_effort, rdo_lambda))

No support for quality levels/RDO.

uint32_t flags = BU_COMP_FLAGS_KTX2_OUTPUT | BU_COMP_FLAGS_KTX2_UASTC_ZSTD | BU_COMP_FLAGS_THREADED;
int old_quality_effort = 1; // use UASTC HDR 4x4 default effort level 1, valid range [0,4]
bu_compress_texture(comp_params, BTF_UASTC_HDR_4X4, -1, -1, flags | old_quality_effort, 0.0f);

Internally the same codec is used for ASTC HDR 6x6 and UASTC HDR 6x6i.

uint32_t flags = BU_COMP_FLAGS_KTX2_OUTPUT | BU_COMP_FLAGS_KTX2_UASTC_ZSTD | BU_COMP_FLAGS_THREADED;
int old_quality_effort = 2; // use RDO ASTC HDR 6x6 default effort level 2, valid range [0,12]
float rdo_lambda = 500.0f; // RDO lambda (0.0f=no RDO, higher=more distortion/smaller files, try roughly [100,10000] for HDR content, or higher for upconverted content)
bu_compress_texture(comp_params, BTF_ASTC_HDR_6X6, -1, -1, flags | old_quality_effort, rdo_lambda);

Internally the same codec is used for ASTC HDR 6x6 and UASTC HDR 6x6i.

uint32_t flags = BU_COMP_FLAGS_KTX2_OUTPUT | BU_COMP_FLAGS_KTX2_UASTC_ZSTD | BU_COMP_FLAGS_THREADED;
int old_quality_effort = 2; // use UASTC HDR 6x6i default effort level 2, valid range [0,12]
float rdo_lambda = 500.0f; // RDO lambda (0.0f=no RDO, higher=more distortion/smaller files, try roughly [100,10000] for HDR content, or higher for upconverted content)
bu_compress_texture(comp_params, BTF_UASTC_HDR_6X6, -1, -1, flags | old_quality_effort, rdo_lambda);

Internally the same codec is used for ASTC LDR 4x4-12x12 and XUASTC LDR 4x4-12x12.

No support for quality levels/RDO via the C API.

uint32_t flags = BU_COMP_FLAGS_KTX2_OUTPUT | BU_COMP_FLAGS_KTX2_UASTC_ZSTD | BU_COMP_FLAGS_THREADED | BU_COMP_FLAGS_SRGB;
int old_effort_level = 2; // use ASTC LDR 4x4-12x12 default effort level 2, valid range [0,10]
float dct_quality = 0.0f; // ASTC LDR doesn't support DCT, so set to 0
bu_compress_texture(comp_params, BTF_ASTC_LDR_4X4, -1, -1, flags | old_effort_level, dct_quality);

Internally the same codec is used for ASTC LDR 4x4-12x12 and XUASTC LDR 4x4-12x12.

uint32_t flags = BU_COMP_FLAGS_KTX2_OUTPUT | BU_COMP_FLAGS_KTX2_UASTC_ZSTD | BU_COMP_FLAGS_THREADED | BU_COMP_FLAGS_SRGB;
int old_effort_level = 2; // use ASTC LDR 4x4-12x12 default effort level 2, valid range [0,10]
float dct_quality = 75.0f; // use DCT level 75, valid range [1,100], 0 or 100=disable DCT, <100 also enables Windowed RDO with default (conservative) settings
bu_compress_texture(comp_params, BTF_XUASTC_LDR_4X4, -1, -1, flags | old_effort_level, dct_quality);

See Also