liquidex
5e6b84bed5
for faster load times, and seamless updates. because for some reason ServeDir can't do it correctly, and it tells the client "yeah hey nothing changed" even if something changed
223 lines
5.9 KiB
JavaScript
223 lines
5.9 KiB
JavaScript
let panicImpl;
|
|
let logImpl;
|
|
|
|
function makeLogFunction(level) {
|
|
return (length, pMessage) => {
|
|
logImpl(level, length, pMessage);
|
|
};
|
|
}
|
|
|
|
let { instance: hakuInstance, module: hakuModule } = await WebAssembly.instantiateStreaming(
|
|
fetch(HAKU_WASM_PATH),
|
|
{
|
|
env: {
|
|
panic(length, pMessage) {
|
|
panicImpl(length, pMessage);
|
|
},
|
|
trace: makeLogFunction("trace"),
|
|
debug: makeLogFunction("debug"),
|
|
info: makeLogFunction("info"),
|
|
warn: makeLogFunction("warn"),
|
|
error: makeLogFunction("error"),
|
|
},
|
|
},
|
|
);
|
|
|
|
let memory = hakuInstance.exports.memory;
|
|
let w = hakuInstance.exports;
|
|
|
|
let textEncoder = new TextEncoder();
|
|
function allocString(string) {
|
|
let size = string.length * 3;
|
|
let align = 1;
|
|
let pString = w.haku_alloc(size, align);
|
|
|
|
let buffer = new Uint8Array(memory.buffer, pString, size);
|
|
let result = textEncoder.encodeInto(string, buffer);
|
|
|
|
return {
|
|
ptr: pString,
|
|
length: result.written,
|
|
size,
|
|
align,
|
|
};
|
|
}
|
|
|
|
function freeString(alloc) {
|
|
w.haku_free(alloc.ptr, alloc.size, alloc.align);
|
|
}
|
|
|
|
let textDecoder = new TextDecoder();
|
|
function readString(size, pString) {
|
|
let buffer = new Uint8Array(memory.buffer, pString, size);
|
|
return textDecoder.decode(buffer);
|
|
}
|
|
|
|
function readCString(pCString) {
|
|
let memoryBuffer = new Uint8Array(memory.buffer);
|
|
|
|
let pCursor = pCString;
|
|
while (memoryBuffer[pCursor] != 0 && memoryBuffer[pCursor] != null) {
|
|
pCursor++;
|
|
}
|
|
|
|
let size = pCursor - pCString;
|
|
return readString(size, pCString);
|
|
}
|
|
|
|
class Panic extends Error {
|
|
name = "Panic";
|
|
}
|
|
|
|
panicImpl = (length, pMessage) => {
|
|
throw new Panic(readString(length, pMessage));
|
|
};
|
|
|
|
logImpl = (level, length, pMessage) => {
|
|
console[level](readString(length, pMessage));
|
|
};
|
|
|
|
w.haku_init_logging();
|
|
|
|
export class Pixmap {
|
|
#pPixmap = 0;
|
|
|
|
constructor(width, height) {
|
|
this.#pPixmap = w.haku_pixmap_new(width, height);
|
|
this.width = width;
|
|
this.height = height;
|
|
}
|
|
|
|
destroy() {
|
|
w.haku_pixmap_destroy(this.#pPixmap);
|
|
}
|
|
|
|
clear(r, g, b, a) {
|
|
w.haku_pixmap_clear(this.#pPixmap, r, g, b, a);
|
|
}
|
|
|
|
get ptr() {
|
|
return this.#pPixmap;
|
|
}
|
|
|
|
getArrayBuffer() {
|
|
return new Uint8ClampedArray(
|
|
memory.buffer,
|
|
w.haku_pixmap_data(this.#pPixmap),
|
|
this.width * this.height * 4,
|
|
);
|
|
}
|
|
|
|
getImageData() {
|
|
return new ImageData(this.getArrayBuffer(), this.width, this.height);
|
|
}
|
|
}
|
|
|
|
export class Haku {
|
|
#pInstance = 0;
|
|
#pBrush = 0;
|
|
#brushCode = null;
|
|
|
|
constructor(limits) {
|
|
console.groupCollapsed("construct Haku");
|
|
|
|
let pLimits = w.haku_limits_new();
|
|
for (let name of Object.keys(limits)) {
|
|
w[`haku_limits_set_${name}`](pLimits, limits[name]);
|
|
}
|
|
|
|
this.#pInstance = w.haku_instance_new(pLimits);
|
|
this.#pBrush = w.haku_brush_new();
|
|
|
|
w.haku_limits_destroy(pLimits);
|
|
|
|
console.groupEnd();
|
|
}
|
|
|
|
destroy() {
|
|
w.haku_brush_destroy(this.#pBrush);
|
|
w.haku_instance_destroy(this.#pInstance);
|
|
}
|
|
|
|
setBrush(code) {
|
|
w.haku_reset(this.#pInstance);
|
|
// NOTE: Brush is invalid at this point, because we reset removes all defs and registered chunks.
|
|
|
|
if (this.#brushCode != null) freeString(this.#brushCode);
|
|
this.#brushCode = allocString(code);
|
|
|
|
let statusCode = w.haku_compile_brush(
|
|
this.#pInstance,
|
|
this.#pBrush,
|
|
this.#brushCode.length,
|
|
this.#brushCode.ptr,
|
|
);
|
|
if (!w.haku_is_ok(statusCode)) {
|
|
if (w.haku_is_diagnostics_emitted(statusCode)) {
|
|
let diagnostics = [];
|
|
for (let i = 0; i < w.haku_num_diagnostics(this.#pBrush); ++i) {
|
|
diagnostics.push({
|
|
start: w.haku_diagnostic_start(this.#pBrush, i),
|
|
end: w.haku_diagnostic_end(this.#pBrush, i),
|
|
message: readString(
|
|
w.haku_diagnostic_message_len(this.#pBrush, i),
|
|
w.haku_diagnostic_message(this.#pBrush, i),
|
|
),
|
|
});
|
|
}
|
|
return {
|
|
status: "error",
|
|
errorKind: "diagnostics",
|
|
diagnostics,
|
|
};
|
|
} else {
|
|
return {
|
|
status: "error",
|
|
errorKind: "plain",
|
|
message: readCString(w.haku_status_string(statusCode)),
|
|
};
|
|
}
|
|
}
|
|
|
|
return { status: "ok" };
|
|
}
|
|
|
|
#statusCodeToResultObject(statusCode) {
|
|
if (!w.haku_is_ok(statusCode)) {
|
|
if (w.haku_is_exception(statusCode)) {
|
|
return {
|
|
status: "error",
|
|
errorKind: "exception",
|
|
description: readCString(w.haku_status_string(statusCode)),
|
|
message: readString(
|
|
w.haku_exception_message_len(this.#pInstance),
|
|
w.haku_exception_message(this.#pInstance),
|
|
),
|
|
};
|
|
} else {
|
|
return {
|
|
status: "error",
|
|
errorKind: "plain",
|
|
message: readCString(w.haku_status_string(statusCode)),
|
|
};
|
|
}
|
|
} else {
|
|
return { status: "ok" };
|
|
}
|
|
}
|
|
|
|
evalBrush() {
|
|
return this.#statusCodeToResultObject(w.haku_eval_brush(this.#pInstance, this.#pBrush));
|
|
}
|
|
|
|
renderValue(pixmap, translationX, translationY) {
|
|
return this.#statusCodeToResultObject(
|
|
w.haku_render_value(this.#pInstance, pixmap.ptr, translationX, translationY),
|
|
);
|
|
}
|
|
|
|
resetVm() {
|
|
w.haku_reset_vm(this.#pInstance);
|
|
}
|
|
}
|