mirror of
https://github.com/BinomialLLC/basis_universal.git
synced 2026-06-08 08:33:53 +00:00
This update impacts the UASTC HDR 4x4, UASTC HDR 6x6i, and XUASTC LDR formats. New KTX2 files in these specific formats using v2.1 can't be loaded using v2.0, however we've kept backwards compatibility with all of our previously written KTX2 files. (v2.1 can transcode all previously written files.) This is part of our effort to be compatible with Khronos's specification. bc6hf encoder's smooth block path updated to handle inputs with texels close to MAX_HALF_FLOAT. Adding command line options to basisu tool so it can write either v2.0 or v1.6 compatible UASTC HDR 6x6i files. The default is v1.6. Updating WebGL KTX2 testbed so on ASTC devices it automatically switches between sRGB or linear internal formats for the created texture.
708 lines
23 KiB
C
708 lines
23 KiB
C
// example_capi.c - Plain C API examples
|
|
// Compresses a procedurally generated 32bpp 512x512 test image to a XUASTC LDR 8x5 .ktx2 file with mipmaps and writes a .ktx2 file.
|
|
// The .ktx2 file is then opened by the transcoder module, examined and unpacked to RGBA 32bpp and ASTC textures which are saved to disk as .tga and .astc files.
|
|
// The .tga image files can be viewed by many common image editors/viewers.
|
|
// The standard .astc texture files can be unpacked to .PNG using ARM's astcenc tool, using a command line like this: astcenc-avx2.exe -ds transcoded_0_0_0.astc 0.png
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <math.h>
|
|
#include <memory.h>
|
|
|
|
typedef int BOOL;
|
|
#define TRUE (1)
|
|
#define FALSE (0)
|
|
|
|
// Include compressor and transcoder C API definitions
|
|
#include "../encoder/basisu_wasm_api.h"
|
|
#include "../encoder/basisu_wasm_transcoder_api.h"
|
|
|
|
// Write a blob of data in memory to a file
|
|
int write_blob_to_file(const char* pFilename, const void* pData, size_t len)
|
|
{
|
|
assert(pFilename != NULL);
|
|
assert(pData != NULL);
|
|
|
|
if (!pFilename || !pData)
|
|
return FALSE;
|
|
|
|
FILE* f = fopen(pFilename, "wb");
|
|
if (!f)
|
|
return FALSE;
|
|
|
|
/* Write the data */
|
|
size_t written = fwrite(pData, 1, len, f);
|
|
if (written != len)
|
|
{
|
|
fclose(f);
|
|
return FALSE;
|
|
}
|
|
|
|
if (fclose(f) != 0)
|
|
return FALSE;
|
|
|
|
return TRUE; /* success */
|
|
}
|
|
|
|
// Writes 24/32bpp .TGA image files
|
|
int write_tga_image(const char* pFilename, int w, int h, int has_alpha, const uint8_t* pPixelsRGBA)
|
|
{
|
|
assert(pFilename != NULL);
|
|
assert(pPixelsRGBA != NULL);
|
|
assert(w > 0);
|
|
assert(h > 0);
|
|
assert((has_alpha == 0) || (has_alpha == 1));
|
|
|
|
/* Runtime argument validation */
|
|
if ((!pFilename) || (!pPixelsRGBA) || (w <= 0) || (h <= 0))
|
|
return -1; // invalid argument
|
|
|
|
FILE* pFile = fopen(pFilename, "wb");
|
|
if (!pFile)
|
|
return -2; // cannot open file
|
|
|
|
uint8_t header[18] = { 0 };
|
|
header[2] = 2; // uncompressed true-color
|
|
header[12] = (uint8_t)(w & 0xFF);
|
|
header[13] = (uint8_t)((w >> 8) & 0xFF);
|
|
header[14] = (uint8_t)(h & 0xFF);
|
|
header[15] = (uint8_t)((h >> 8) & 0xFF);
|
|
header[16] = has_alpha ? 32 : 24;
|
|
|
|
/* Classic TGA: bottom-left origin */
|
|
header[17] = has_alpha ? 8 : 0;
|
|
|
|
if (fwrite(header, 1, 18, pFile) != 18)
|
|
{
|
|
fclose(pFile);
|
|
return -3; // header write failed
|
|
}
|
|
|
|
uint64_t bytes_per_pixel = has_alpha ? 4ULL : 3ULL;
|
|
uint64_t pixel_bytes_u64 = (uint64_t)w * (uint64_t)h * bytes_per_pixel;
|
|
size_t pixel_bytes = (size_t)pixel_bytes_u64;
|
|
|
|
if ((uint64_t)pixel_bytes != pixel_bytes_u64)
|
|
return -6; // overflow bogus dimensions
|
|
|
|
/* allocate one scanline for BGRA/BGR output */
|
|
size_t row_bytes = (size_t)((size_t)w * bytes_per_pixel);
|
|
uint8_t* pRow = (uint8_t*)malloc(row_bytes);
|
|
if (!pRow)
|
|
{
|
|
fclose(pFile);
|
|
return -7; // out of memory
|
|
}
|
|
|
|
/* TGA expects rows in bottom-to-top order */
|
|
for (int y = 0; y < h; y++)
|
|
{
|
|
const uint8_t* pSrcRow = pPixelsRGBA + (size_t)(h - 1 - y) * w * bytes_per_pixel;
|
|
|
|
/* Convert RGBA->BGRA or RGB->BGR for this row */
|
|
if (has_alpha)
|
|
{
|
|
/* 4 bytes per pixel */
|
|
for (int x = 0; x < w; x++)
|
|
{
|
|
const uint8_t* s = &pSrcRow[x * 4];
|
|
uint8_t* d = &pRow[x * 4];
|
|
|
|
d[0] = s[2]; // B
|
|
d[1] = s[1]; // G
|
|
d[2] = s[0]; // R
|
|
d[3] = s[3]; // A
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* 3 bytes per pixel */
|
|
for (int x = 0; x < w; x++)
|
|
{
|
|
const uint8_t* s = &pSrcRow[x * 3];
|
|
uint8_t* d = &pRow[x * 3];
|
|
|
|
d[0] = s[2]; // B
|
|
d[1] = s[1]; // G
|
|
d[2] = s[0]; // R
|
|
}
|
|
}
|
|
|
|
if (fwrite(pRow, 1, row_bytes, pFile) != row_bytes)
|
|
{
|
|
free(pRow);
|
|
fclose(pFile);
|
|
return -4; // pixel write failed
|
|
}
|
|
}
|
|
|
|
free(pRow);
|
|
|
|
if (fclose(pFile) != 0)
|
|
return -5; // close failed
|
|
|
|
return 0; // success
|
|
}
|
|
|
|
// Write standard ARM .ASTC format texture files
|
|
int write_astc_file(const char* pFilename,
|
|
const void* pBlocks, // pointer to ASTC blocks
|
|
uint32_t block_width, // in texels [4,12]
|
|
uint32_t block_height, // in texels [4,12]
|
|
uint32_t dim_x, // image actual dimension in texels
|
|
uint32_t dim_y) // image actual dimension in texels
|
|
{
|
|
assert(pFilename != NULL);
|
|
assert(pBlocks != NULL);
|
|
assert(dim_x > 0);
|
|
assert(dim_y > 0);
|
|
assert((block_width >= 4) && (block_width <= 12));
|
|
assert((block_height >= 4) && (block_height <= 12));
|
|
|
|
FILE* f = fopen(pFilename, "wb");
|
|
if (!f)
|
|
return 0;
|
|
|
|
/* Helper macro for writing single bytes with error check */
|
|
#define PUTB(v) do { if (fputc((int)(v), f) == EOF) { fclose(f); return 0; } } while (0)
|
|
|
|
/* Magic */
|
|
PUTB(0x13);
|
|
PUTB(0xAB);
|
|
PUTB(0xA1);
|
|
PUTB(0x5C);
|
|
|
|
/* Block dimensions: x, y, z = 1 */
|
|
PUTB((uint8_t)block_width);
|
|
PUTB((uint8_t)block_height);
|
|
PUTB(1); /* block depth */
|
|
|
|
/* dim_x (24-bit little endian) */
|
|
PUTB((uint8_t)(dim_x & 0xFF));
|
|
PUTB((uint8_t)((dim_x >> 8) & 0xFF));
|
|
PUTB((uint8_t)((dim_x >> 16) & 0xFF));
|
|
|
|
/* dim_y (24-bit little endian) */
|
|
PUTB((uint8_t)(dim_y & 0xFF));
|
|
PUTB((uint8_t)((dim_y >> 8) & 0xFF));
|
|
PUTB((uint8_t)((dim_y >> 16) & 0xFF));
|
|
|
|
/* dim_z = 1 (24-bit LE) */
|
|
PUTB(1);
|
|
PUTB(0);
|
|
PUTB(0);
|
|
|
|
/* Compute block count and total bytes */
|
|
uint32_t num_blocks_x = (dim_x + block_width - 1) / block_width;
|
|
uint32_t num_blocks_y = (dim_y + block_height - 1) / block_height;
|
|
|
|
uint64_t total_bytes_u64 =
|
|
(uint64_t)num_blocks_x * (uint64_t)num_blocks_y * 16ULL;
|
|
|
|
size_t total_bytes = (size_t)total_bytes_u64;
|
|
|
|
if ((uint64_t)total_bytes != total_bytes_u64)
|
|
{
|
|
fclose(f);
|
|
return 0; /* overflow → fail */
|
|
}
|
|
|
|
/* Write block data directly */
|
|
size_t written = fwrite(pBlocks, 1, total_bytes, f);
|
|
if (written != total_bytes)
|
|
{
|
|
fclose(f); /* still close even if error */
|
|
return 0;
|
|
}
|
|
|
|
if (fclose(f) != 0)
|
|
return 0;
|
|
|
|
return 1; /* success */
|
|
|
|
#undef PUTB
|
|
}
|
|
|
|
// Procedurally create a simple test image in memory
|
|
uint8_t* create_pretty_rgba_pattern(int w, int h, float q)
|
|
{
|
|
if (w <= 0 || h <= 0)
|
|
return NULL;
|
|
|
|
uint8_t* pImage = (uint8_t*)malloc((size_t)w * h * 4);
|
|
if (!pImage)
|
|
return NULL;
|
|
|
|
for (int y = 0; y < h; y++)
|
|
{
|
|
for (int x = 0; x < w; x++)
|
|
{
|
|
/* normalized coordinates 0..1 */
|
|
float fx = (float)x / (float)w;
|
|
float fy = (float)y / (float)h;
|
|
|
|
/* --- Extra coordinate warping when q != 0 --- */
|
|
if (q != 0.0f) {
|
|
float warp = sinf((fx + fy) * 10.0f * q);
|
|
fx += 0.15f * q * warp;
|
|
fy += 0.15f * q * sinf((fx - fy) * 8.0f * q);
|
|
}
|
|
|
|
/* Original plasma formula */
|
|
float v = sinf(fx * 12.0f + fy * 4.0f);
|
|
v += sinf(fy * 9.0f - fx * 6.0f);
|
|
v += sinf((fx + fy) * 7.0f);
|
|
|
|
/* Extra variation term — contributes only when q != 0 */
|
|
if (q != 0.0f)
|
|
{
|
|
v += q * 0.7f * sinf((fx * fx + fy) * 20.0f);
|
|
v += q * 0.4f * cosf((fx - fy) * 18.0f);
|
|
}
|
|
|
|
/* scale to 0..1 */
|
|
v = v * 0.25f + 0.5f;
|
|
|
|
float L = 1.5f;
|
|
|
|
/* Convert to RGB colors */
|
|
int r = (int)roundf(255.0f * sinf(v * 6.28f) * L);
|
|
int g = (int)roundf(255.0f * (1.0f - v) * L);
|
|
int b = (int)roundf(255.0f * v * L);
|
|
|
|
/* clamp */
|
|
if (r < 0) r = 0; else if (r > 255) r = 255;
|
|
if (g < 0) g = 0; else if (g > 255) g = 255;
|
|
if (b < 0) b = 0; else if (b > 255) b = 255;
|
|
|
|
/* write RGBA */
|
|
uint8_t* p = &pImage[(y * w + x) * 4];
|
|
p[0] = (uint8_t)r;
|
|
p[1] = (uint8_t)g;
|
|
p[2] = (uint8_t)b;
|
|
p[3] = 255;
|
|
}
|
|
}
|
|
|
|
return pImage;
|
|
}
|
|
|
|
// Takes a KTX2 file in memory and displays info about it, then transcodes it to RGBA32 and ASTC, writing .tga/.astc files to disk
|
|
int transcode_ktx2_file(const void* pKTX2_data, size_t ktx2_data_size, const char *pDesc)
|
|
{
|
|
printf("------ transcode_ktx2_file(): ktx2 size: %zu, desc: %s\n", ktx2_data_size, pDesc);
|
|
|
|
if (!pKTX2_data || !ktx2_data_size)
|
|
return FALSE;
|
|
|
|
if ((uint32_t)ktx2_data_size != ktx2_data_size)
|
|
return FALSE;
|
|
|
|
uint64_t ktx2_data_ofs = bt_alloc(ktx2_data_size);
|
|
if (!ktx2_data_ofs)
|
|
return FALSE;
|
|
|
|
memcpy((void*)ktx2_data_ofs, pKTX2_data, ktx2_data_size);
|
|
|
|
uint64_t ktx2_handle = bt_ktx2_open(ktx2_data_ofs, (uint32_t)ktx2_data_size);
|
|
if (!ktx2_handle)
|
|
{
|
|
bt_free(ktx2_data_ofs);
|
|
return FALSE;
|
|
}
|
|
|
|
// Just testing LDR here for now
|
|
if (!bt_ktx2_is_ldr(ktx2_handle))
|
|
{
|
|
bt_ktx2_close(ktx2_handle);
|
|
bt_free(ktx2_data_ofs);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!bt_ktx2_start_transcoding(ktx2_handle))
|
|
{
|
|
bt_ktx2_close(ktx2_handle);
|
|
bt_free(ktx2_data_ofs);
|
|
return FALSE;
|
|
}
|
|
|
|
uint32_t width = bt_ktx2_get_width(ktx2_handle), height = bt_ktx2_get_height(ktx2_handle);
|
|
uint32_t levels = bt_ktx2_get_levels(ktx2_handle); // number of mipmap levels, must be >= 1
|
|
uint32_t faces = bt_ktx2_get_faces(ktx2_handle); // 1 or 6
|
|
uint32_t layers = bt_ktx2_get_layers(ktx2_handle); // 0 or array size
|
|
|
|
uint32_t basis_tex_format = bt_ktx2_get_basis_tex_format(ktx2_handle);
|
|
uint32_t block_width = bt_ktx2_get_block_width(ktx2_handle);
|
|
uint32_t block_height = bt_ktx2_get_block_height(ktx2_handle);
|
|
uint32_t is_srgb = bt_ktx2_is_srgb(ktx2_handle);
|
|
uint32_t is_video = bt_ktx2_is_video(ktx2_handle); // only reliably set after calling bt_ktx2_start_transcoding()
|
|
|
|
printf("KTX2 Dimensions: %ux%u, Levels: %u, Faces: %u, Layers: %u\n", width, height, levels, faces, layers);
|
|
printf("basis_tex_format: %u\n", basis_tex_format);
|
|
printf("Block dimensions: %ux%u\n", block_width, block_height);
|
|
printf("is sRGB: %u\n", is_srgb);
|
|
printf("is video: %u\n", is_video);
|
|
|
|
assert((width >= 1) && (height >= 1));
|
|
assert(levels >= 1);
|
|
assert((faces == 6) || (faces == 1));
|
|
|
|
// If layers==0 it's not a texture array
|
|
if (layers < 1)
|
|
layers = 1;
|
|
|
|
// Create our transcoding state handle (which contains thread-local state)
|
|
// This is actually optional, and only needed for thread-safe transcoding, but we'll test it here.
|
|
uint64_t transcode_state_handle = bt_ktx2_create_transcode_state();
|
|
|
|
for (uint32_t level_index = 0; level_index < levels; level_index++)
|
|
{
|
|
for (uint32_t layer_index = 0; layer_index < layers; layer_index++)
|
|
{
|
|
for (uint32_t face_index = 0; face_index < faces; face_index++)
|
|
{
|
|
printf("- Level: %u, layer: %u, face: %u\n", level_index, layer_index, face_index);
|
|
|
|
uint32_t orig_width = bt_ktx2_get_level_orig_width(ktx2_handle, level_index, layer_index, face_index);
|
|
uint32_t orig_height = bt_ktx2_get_level_orig_height(ktx2_handle, level_index, layer_index, face_index);
|
|
|
|
printf(" Orig dimensions: %ux%u, actual: %ux%u\n",
|
|
orig_width, orig_height,
|
|
bt_ktx2_get_level_actual_width(ktx2_handle, level_index, layer_index, face_index), bt_ktx2_get_level_actual_height(ktx2_handle, level_index, layer_index, face_index));
|
|
|
|
printf(" Block dimensions: %ux%u, total blocks: %u\n",
|
|
bt_ktx2_get_level_num_blocks_x(ktx2_handle, level_index, layer_index, face_index),
|
|
bt_ktx2_get_level_num_blocks_y(ktx2_handle, level_index, layer_index, face_index),
|
|
bt_ktx2_get_level_total_blocks(ktx2_handle, level_index, layer_index, face_index));
|
|
|
|
printf(" Alpha flag: %u, iframe flag: %u\n",
|
|
bt_ktx2_get_level_alpha_flag(ktx2_handle, level_index, layer_index, face_index),
|
|
bt_ktx2_get_level_iframe_flag(ktx2_handle, level_index, layer_index, face_index));
|
|
|
|
// First transcode level to uncompressed RGBA32 and write a .tga file
|
|
{
|
|
char tga_filename[256];
|
|
snprintf(tga_filename, sizeof(tga_filename), "transcoded_%s_L%u_Y%u_F%u.tga", pDesc, level_index, layer_index, face_index);
|
|
|
|
uint32_t transcode_buf_size = bt_basis_compute_transcoded_image_size_in_bytes(TF_RGBA32, orig_width, orig_height);
|
|
assert(transcode_buf_size);
|
|
|
|
uint64_t transcode_buf_ofs = bt_alloc(transcode_buf_size);
|
|
|
|
uint32_t decode_flags = 0;
|
|
|
|
if (!bt_ktx2_transcode_image_level(ktx2_handle, level_index, layer_index, face_index,
|
|
transcode_buf_ofs, transcode_buf_size / sizeof(uint32_t), // it wants blocks or pixels, not bytes
|
|
TF_RGBA32,
|
|
decode_flags,
|
|
0, 0, -1, -1, transcode_state_handle))
|
|
{
|
|
bt_free(transcode_buf_ofs);
|
|
bt_ktx2_destroy_transcode_state(transcode_state_handle);
|
|
bt_ktx2_close(ktx2_handle);
|
|
bt_free(ktx2_data_ofs);
|
|
return FALSE;
|
|
}
|
|
|
|
write_tga_image(tga_filename, orig_width, orig_height, TRUE, (uint8_t*)transcode_buf_ofs);
|
|
printf("Wrote file %s\n", tga_filename);
|
|
|
|
bt_free(transcode_buf_ofs);
|
|
transcode_buf_ofs = 0;
|
|
}
|
|
|
|
// Now transcode to ASTC and write a .astc file
|
|
{
|
|
char astc_filename[256];
|
|
snprintf(astc_filename, sizeof(astc_filename), "transcoded_%s_L%u_Y%u_F%u.astc", pDesc, level_index, layer_index, face_index);
|
|
|
|
// Determine the correct ASTC transcode texture format from the ktx2 format
|
|
uint32_t target_transcode_fmt = bt_basis_get_transcoder_texture_format_from_basis_tex_format(basis_tex_format);
|
|
|
|
uint32_t transcode_buf_size = bt_basis_compute_transcoded_image_size_in_bytes(target_transcode_fmt, orig_width, orig_height);
|
|
assert(transcode_buf_size);
|
|
|
|
uint64_t transcode_buf_ofs = bt_alloc(transcode_buf_size);
|
|
|
|
uint32_t decode_flags = 0;
|
|
|
|
if (!bt_ktx2_transcode_image_level(ktx2_handle, level_index, layer_index, face_index,
|
|
transcode_buf_ofs, transcode_buf_size / 16, // API wants blocks or pixels, not bytes - ASTC is always 16 bytes per block
|
|
target_transcode_fmt,
|
|
decode_flags,
|
|
0, 0, -1, -1, transcode_state_handle))
|
|
{
|
|
bt_free(transcode_buf_ofs);
|
|
bt_ktx2_destroy_transcode_state(transcode_state_handle);
|
|
bt_ktx2_close(ktx2_handle);
|
|
bt_free(ktx2_data_ofs);
|
|
return FALSE;
|
|
}
|
|
|
|
write_astc_file(astc_filename, (void*)transcode_buf_ofs, block_width, block_height, orig_width, orig_height);
|
|
printf("Wrote .astc file %s\n", astc_filename);
|
|
|
|
bt_free(transcode_buf_ofs);
|
|
transcode_buf_ofs = 0;
|
|
}
|
|
|
|
} // face_index
|
|
|
|
} // layer_index
|
|
|
|
} // level_index
|
|
|
|
bt_ktx2_destroy_transcode_state(transcode_state_handle);
|
|
transcode_state_handle = 0;
|
|
|
|
bt_ktx2_close(ktx2_handle);
|
|
ktx2_handle = 0;
|
|
|
|
bt_free(ktx2_data_ofs);
|
|
ktx2_data_ofs = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Simple 2D test
|
|
int test_2D()
|
|
{
|
|
printf("------ test_2D():\n");
|
|
|
|
// Generate a test image
|
|
int W = 512, H = 512;
|
|
|
|
uint8_t* pSrc_image = create_pretty_rgba_pattern(W, H, 0.0f);
|
|
|
|
// Save the test image to a .tga file
|
|
write_tga_image("test_image.tga", W, H, TRUE, pSrc_image);
|
|
printf("Wrote file test_image.tga\n");
|
|
|
|
// Compress it to .ktx2
|
|
uint64_t comp_params = bu_new_comp_params();
|
|
|
|
// Allocate memory
|
|
uint64_t img_ofs = bu_alloc(W * H * 4);
|
|
if (!img_ofs)
|
|
{
|
|
fprintf(stderr, "bu_alloc() failed\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
// Copy the test image into the allocated memory
|
|
memcpy((void*)img_ofs, pSrc_image, W * H * 4);
|
|
|
|
// Supply the image to the compressor - it'll immediately make a copy of the data
|
|
if (!bu_comp_params_set_image_rgba32(comp_params, 0, img_ofs, W, H, W * 4))
|
|
{
|
|
fprintf(stderr, "bu_comp_params_set_image_rgba32() failed\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
bu_free(img_ofs);
|
|
img_ofs = 0;
|
|
|
|
// Now compress it to XUASTC LDR 8x5 with weight grid DCT
|
|
uint32_t basis_tex_format = BTF_XUASTC_LDR_8X5;
|
|
//uint32_t basis_tex_format = BTF_ASTC_LDR_8X5;
|
|
//uint32_t basis_tex_format = BTF_ETC1S;
|
|
//uint32_t basis_tex_format = BTF_UASTC_LDR_4X4;
|
|
|
|
uint32_t quality_level = 85;
|
|
uint32_t effort_level = 2;
|
|
|
|
uint32_t flags = BU_COMP_FLAGS_KTX2_OUTPUT | BU_COMP_FLAGS_SRGB |
|
|
BU_COMP_FLAGS_THREADED | BU_COMP_FLAGS_GEN_MIPS_CLAMP |
|
|
BU_COMP_FLAGS_PRINT_STATS | BU_COMP_FLAGS_PRINT_STATUS;
|
|
|
|
if (!bu_compress_texture(comp_params, basis_tex_format, quality_level, effort_level, flags, 0.0f))
|
|
{
|
|
fprintf(stderr, "bu_compress_texture() failed\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
// Retrieve the compressed .KTX2 file data
|
|
uint64_t comp_size = bu_comp_params_get_comp_data_size(comp_params);
|
|
if (!comp_size)
|
|
{
|
|
fprintf(stderr, "bu_comp_params_get_comp_data_size() failed\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
void* pComp_data = (void*)bu_comp_params_get_comp_data_ofs(comp_params);
|
|
if (!pComp_data)
|
|
{
|
|
fprintf(stderr, "bu_comp_params_get_comp_data_ofs() failed\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
// Write the data to disk
|
|
write_blob_to_file("test.ktx2", pComp_data, (size_t)comp_size);
|
|
printf("Wrote file test.ktx2\n");
|
|
|
|
// Now inspect and transcode the .KTX2 data to png/astc files
|
|
if (!transcode_ktx2_file(pComp_data, (size_t)comp_size, "2D"))
|
|
{
|
|
fprintf(stderr, "transcode_ktx2_file() failed\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
bu_delete_comp_params(comp_params);
|
|
|
|
free(pSrc_image);
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
// 2D array/texture video test
|
|
int test_2D_array(BOOL tex_video_flag, int L, BOOL mipmap_flag)
|
|
{
|
|
printf("------ test_2D_array() %i %i %i:\n", tex_video_flag, L, mipmap_flag);
|
|
|
|
// Generate a test image
|
|
int W = 256, H = 256;
|
|
|
|
// Compress it to .ktx2
|
|
uint64_t comp_params = bu_new_comp_params();
|
|
|
|
const char* pDesc = tex_video_flag ? "video" : "array";
|
|
|
|
char filename_buf[256];
|
|
|
|
for (int layer = 0; layer < L; layer++)
|
|
{
|
|
uint8_t* pSrc_image = create_pretty_rgba_pattern(W, H, (float)layer * .05f);
|
|
|
|
// Save the test image to a .tga file
|
|
snprintf(filename_buf, sizeof(filename_buf), "test_%s_layer_%u.tga", pDesc, layer);
|
|
|
|
write_tga_image(filename_buf, W, H, TRUE, pSrc_image);
|
|
printf("Wrote file %s\n", filename_buf);
|
|
|
|
// Allocate memory
|
|
uint64_t img_ofs = bu_alloc(W * H * 4);
|
|
if (!img_ofs)
|
|
{
|
|
fprintf(stderr, "bu_alloc() failed\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
// Copy the test image into the allocated memory
|
|
memcpy((void*)img_ofs, pSrc_image, W * H * 4);
|
|
|
|
// Supply the image to the compressor - it'll immediately make a copy of the data
|
|
if (!bu_comp_params_set_image_rgba32(comp_params, layer, img_ofs, W, H, W * 4))
|
|
{
|
|
fprintf(stderr, "bu_comp_params_set_image_rgba32() failed\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
bu_free(img_ofs);
|
|
img_ofs = 0;
|
|
|
|
free(pSrc_image);
|
|
|
|
} // layer
|
|
|
|
// ETC1S has special optimizations for texture video (basic p-frames with skip blocks).
|
|
uint32_t basis_tex_format = tex_video_flag ? BTF_ETC1S : BTF_XUASTC_LDR_4X4;
|
|
|
|
uint32_t quality_level = 100;
|
|
uint32_t effort_level = 4;
|
|
|
|
uint32_t flags = BU_COMP_FLAGS_KTX2_OUTPUT | BU_COMP_FLAGS_SRGB |
|
|
BU_COMP_FLAGS_THREADED |
|
|
BU_COMP_FLAGS_PRINT_STATS | BU_COMP_FLAGS_PRINT_STATUS;
|
|
|
|
if (tex_video_flag)
|
|
flags |= BU_COMP_FLAGS_TEXTURE_TYPE_VIDEO_FRAMES;
|
|
else
|
|
flags |= BU_COMP_FLAGS_TEXTURE_TYPE_2D_ARRAY;
|
|
|
|
if (mipmap_flag)
|
|
flags |= BU_COMP_FLAGS_GEN_MIPS_CLAMP;
|
|
|
|
if (!bu_compress_texture(comp_params, basis_tex_format, quality_level, effort_level, flags, 0.0f))
|
|
{
|
|
fprintf(stderr, "bu_compress_texture() failed\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
// Retrieve the compressed .KTX2 file data
|
|
uint64_t comp_size = bu_comp_params_get_comp_data_size(comp_params);
|
|
if (!comp_size)
|
|
{
|
|
fprintf(stderr, "bu_comp_params_get_comp_data_size() failed\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
void* pComp_data = (void*)bu_comp_params_get_comp_data_ofs(comp_params);
|
|
if (!pComp_data)
|
|
{
|
|
fprintf(stderr, "bu_comp_params_get_comp_data_ofs() failed\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
// Write the data to disk
|
|
snprintf(filename_buf, sizeof(filename_buf), "test_%s.ktx2", pDesc);
|
|
write_blob_to_file(filename_buf, pComp_data, (size_t)comp_size);
|
|
printf("Wrote file %s\n", filename_buf);
|
|
|
|
// Now inspect and transcode the .KTX2 data to png/astc files
|
|
if (!transcode_ktx2_file(pComp_data, (size_t)comp_size, pDesc))
|
|
{
|
|
fprintf(stderr, "transcode_ktx2_file() failed\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
bu_delete_comp_params(comp_params);
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
(void)argc;
|
|
(void)argv;
|
|
printf("example_capi.c:\n");
|
|
|
|
// Initialize the encoder (which initializers the transcoder for us)
|
|
printf("bu_init:\n");
|
|
bu_init();
|
|
|
|
// bu_init() already does this for us, but it's harmless to call again.
|
|
printf("bt_init:\n");
|
|
bt_init();
|
|
|
|
// Control debug output from the compressor
|
|
bu_enable_debug_printf(FALSE);
|
|
|
|
// simple 2D
|
|
if (test_2D() != EXIT_SUCCESS)
|
|
{
|
|
fprintf(stderr, "test_2D() failed!\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
// 2D array
|
|
if (test_2D_array(FALSE, 8, FALSE) != EXIT_SUCCESS)
|
|
{
|
|
fprintf(stderr, "test_2D_array() (array mode) failed!\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
// texture video
|
|
if (test_2D_array(TRUE, 8, TRUE) != EXIT_SUCCESS)
|
|
{
|
|
fprintf(stderr, "test_2D_array() (texture video mode) failed!\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
printf("Success\n");
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
|