| # 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==-1, then rdo_quality (a low-level parameter) directly | |
| # controls each codec's quality setting. Normally set to 0. | |
| 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=0.0): | |
| 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]) |