diff --git a/README.md b/README.md index 2f99f5a6..e7189c37 100644 --- a/README.md +++ b/README.md @@ -24,3 +24,7 @@ Everything else is written in Rust, compiled to WASM. Make an "any" editor. Store text files, make a VERY SIMPLE text editor, in the web. Supports MARKDEEP. A place for you ideas. Stored on your computer (local storage). + +# Credits / Licenses + +ProggyClean.ttf, Copyright (c) 2004, 2005 Tristan Grimmer. MIT License. http://www.proggyfonts.net/ diff --git a/build.sh b/build.sh index 40409d83..023e9e8a 100755 --- a/build.sh +++ b/build.sh @@ -26,7 +26,7 @@ function build_rust echo "Generate JS bindings for wasm:" FOLDER_NAME=${PWD##*/} - TARGET_NAME="$FOLDER_NAME.wasm" + TARGET_NAME="emgui_wasm.wasm" wasm-bindgen "target/wasm32-unknown-unknown/$BUILD/$TARGET_NAME" \ --out-dir docs --no-modules # --no-modules-global hoboho diff --git a/docs/emgui.d.ts b/docs/emgui.d.ts deleted file mode 100644 index 2614fc9a..00000000 --- a/docs/emgui.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -/* tslint:disable */ -export function show_gui(arg0: string): string; - diff --git a/docs/emgui.js b/docs/emgui.js deleted file mode 100644 index 7ae8bf76..00000000 --- a/docs/emgui.js +++ /dev/null @@ -1,102 +0,0 @@ -(function() { - var wasm; - const __exports = {}; - - - let cachedTextEncoder = new TextEncoder('utf-8'); - - let cachegetUint8Memory = null; - function getUint8Memory() { - if (cachegetUint8Memory === null || cachegetUint8Memory.buffer !== wasm.memory.buffer) { - cachegetUint8Memory = new Uint8Array(wasm.memory.buffer); - } - return cachegetUint8Memory; - } - - let WASM_VECTOR_LEN = 0; - - function passStringToWasm(arg) { - - const buf = cachedTextEncoder.encode(arg); - const ptr = wasm.__wbindgen_malloc(buf.length); - getUint8Memory().set(buf, ptr); - WASM_VECTOR_LEN = buf.length; - return ptr; - } - - let cachedTextDecoder = new TextDecoder('utf-8'); - - function getStringFromWasm(ptr, len) { - return cachedTextDecoder.decode(getUint8Memory().subarray(ptr, ptr + len)); - } - - let cachedGlobalArgumentPtr = null; - function globalArgumentPtr() { - if (cachedGlobalArgumentPtr === null) { - cachedGlobalArgumentPtr = wasm.__wbindgen_global_argument_ptr(); - } - return cachedGlobalArgumentPtr; - } - - let cachegetUint32Memory = null; - function getUint32Memory() { - if (cachegetUint32Memory === null || cachegetUint32Memory.buffer !== wasm.memory.buffer) { - cachegetUint32Memory = new Uint32Array(wasm.memory.buffer); - } - return cachegetUint32Memory; - } - /** - * @param {string} arg0 - * @returns {string} - */ - __exports.show_gui = function(arg0) { - const ptr0 = passStringToWasm(arg0); - const len0 = WASM_VECTOR_LEN; - const retptr = globalArgumentPtr(); - try { - wasm.show_gui(retptr, ptr0, len0); - const mem = getUint32Memory(); - const rustptr = mem[retptr / 4]; - const rustlen = mem[retptr / 4 + 1]; - - const realRet = getStringFromWasm(rustptr, rustlen).slice(); - wasm.__wbindgen_free(rustptr, rustlen * 1); - return realRet; - - - } finally { - wasm.__wbindgen_free(ptr0, len0 * 1); - - } - - }; - - __exports.__wbindgen_throw = function(ptr, len) { - throw new Error(getStringFromWasm(ptr, len)); - }; - - function init(path_or_module) { - let instantiation; - const imports = { './emgui': __exports }; - if (path_or_module instanceof WebAssembly.Module) { - instantiation = WebAssembly.instantiate(path_or_module, imports) - .then(instance => { - return { instance, module: path_or_module } - }); - } else { - const data = fetch(path_or_module); - if (typeof WebAssembly.instantiateStreaming === 'function') { - instantiation = WebAssembly.instantiateStreaming(data, imports); - } else { - instantiation = data - .then(response => response.arrayBuffer()) - .then(buffer => WebAssembly.instantiate(buffer, imports)); - } - } - return instantiation.then(({instance}) => { - wasm = init.wasm = instance.exports; - - }); -}; -self.wasm_bindgen = Object.assign(init, __exports); -})(); diff --git a/docs/emgui_bg.wasm b/docs/emgui_bg.wasm deleted file mode 100644 index 62a3c82b..00000000 Binary files a/docs/emgui_bg.wasm and /dev/null differ diff --git a/docs/emgui_wasm.js b/docs/emgui_wasm.js new file mode 100644 index 00000000..a9c002cf --- /dev/null +++ b/docs/emgui_wasm.js @@ -0,0 +1,561 @@ +(function() { + var wasm; + const __exports = {}; + + + let cachedTextEncoder = new TextEncoder('utf-8'); + + let cachegetUint8Memory = null; + function getUint8Memory() { + if (cachegetUint8Memory === null || cachegetUint8Memory.buffer !== wasm.memory.buffer) { + cachegetUint8Memory = new Uint8Array(wasm.memory.buffer); + } + return cachegetUint8Memory; + } + + let WASM_VECTOR_LEN = 0; + + function passStringToWasm(arg) { + + const buf = cachedTextEncoder.encode(arg); + const ptr = wasm.__wbindgen_malloc(buf.length); + getUint8Memory().set(buf, ptr); + WASM_VECTOR_LEN = buf.length; + return ptr; + } + + let cachedTextDecoder = new TextDecoder('utf-8'); + + function getStringFromWasm(ptr, len) { + return cachedTextDecoder.decode(getUint8Memory().subarray(ptr, ptr + len)); + } + + let cachedGlobalArgumentPtr = null; + function globalArgumentPtr() { + if (cachedGlobalArgumentPtr === null) { + cachedGlobalArgumentPtr = wasm.__wbindgen_global_argument_ptr(); + } + return cachedGlobalArgumentPtr; + } + + let cachegetUint32Memory = null; + function getUint32Memory() { + if (cachegetUint32Memory === null || cachegetUint32Memory.buffer !== wasm.memory.buffer) { + cachegetUint32Memory = new Uint32Array(wasm.memory.buffer); + } + return cachegetUint32Memory; + } + /** + * @param {string} arg0 + * @returns {string} + */ + __exports.show_gui = function(arg0) { + const ptr0 = passStringToWasm(arg0); + const len0 = WASM_VECTOR_LEN; + const retptr = globalArgumentPtr(); + try { + wasm.show_gui(retptr, ptr0, len0); + const mem = getUint32Memory(); + const rustptr = mem[retptr / 4]; + const rustlen = mem[retptr / 4 + 1]; + + const realRet = getStringFromWasm(rustptr, rustlen).slice(); + wasm.__wbindgen_free(rustptr, rustlen * 1); + return realRet; + + + } finally { + wasm.__wbindgen_free(ptr0, len0 * 1); + + } + + }; + + /** + * @param {string} arg0 + * @returns {Painter} + */ + __exports.new_webgl_painter = function(arg0) { + const ptr0 = passStringToWasm(arg0); + const len0 = WASM_VECTOR_LEN; + try { + return Painter.__wrap(wasm.new_webgl_painter(ptr0, len0)); + + } finally { + wasm.__wbindgen_free(ptr0, len0 * 1); + + } + + }; + + /** + * @param {Painter} arg0 + * @param {string} arg1 + * @returns {void} + */ + __exports.paint_webgl = function(arg0, arg1) { + const ptr1 = passStringToWasm(arg1); + const len1 = WASM_VECTOR_LEN; + try { + return wasm.paint_webgl(arg0.ptr, ptr1, len1); + + } finally { + wasm.__wbindgen_free(ptr1, len1 * 1); + + } + + }; + + const heap = new Array(32); + + heap.fill(undefined); + + heap.push(undefined, null, true, false); + +function getObject(idx) { return heap[idx]; } + +let heap_next = heap.length; + +function addHeapObject(obj) { + if (heap_next === heap.length) heap.push(heap.length + 1); + const idx = heap_next; + heap_next = heap[idx]; + + heap[idx] = obj; + return idx; +} + +function isLikeNone(x) { + return x === undefined || x === null; +} + +const __widl_f_get_element_by_id_Document_target = typeof Document === 'undefined' ? null : Document.prototype.getElementById || function() { + throw new Error(`wasm-bindgen: Document.getElementById does not exist`); +}; + +__exports.__widl_f_get_element_by_id_Document = function(arg0, arg1, arg2) { + let varg1 = getStringFromWasm(arg1, arg2); + + const val = __widl_f_get_element_by_id_Document_target.call(getObject(arg0), varg1); + return isLikeNone(val) ? 0 : addHeapObject(val); + +}; + +__exports.__widl_instanceof_HTMLCanvasElement = function(idx) { + return getObject(idx) instanceof HTMLCanvasElement ? 1 : 0; +}; + +const __widl_f_get_context_HTMLCanvasElement_target = typeof HTMLCanvasElement === 'undefined' ? null : HTMLCanvasElement.prototype.getContext || function() { + throw new Error(`wasm-bindgen: HTMLCanvasElement.getContext does not exist`); +}; + +__exports.__widl_f_get_context_HTMLCanvasElement = function(arg0, arg1, arg2, exnptr) { + let varg1 = getStringFromWasm(arg1, arg2); + try { + + const val = __widl_f_get_context_HTMLCanvasElement_target.call(getObject(arg0), varg1); + return isLikeNone(val) ? 0 : addHeapObject(val); + + } catch (e) { + const view = getUint32Memory(); + view[exnptr / 4] = 1; + view[exnptr / 4 + 1] = addHeapObject(e); + + } +}; + +function GetOwnOrInheritedPropertyDescriptor(obj, id) { + while (obj) { + let desc = Object.getOwnPropertyDescriptor(obj, id); + if (desc) return desc; + obj = Object.getPrototypeOf(obj); + } +return {} +} + +const __widl_f_width_HTMLCanvasElement_target = GetOwnOrInheritedPropertyDescriptor(typeof HTMLCanvasElement === 'undefined' ? null : HTMLCanvasElement.prototype, 'width').get || function() { + throw new Error(`wasm-bindgen: HTMLCanvasElement.width does not exist`); +}; + +__exports.__widl_f_width_HTMLCanvasElement = function(arg0) { + return __widl_f_width_HTMLCanvasElement_target.call(getObject(arg0)); +}; + +const __widl_f_height_HTMLCanvasElement_target = GetOwnOrInheritedPropertyDescriptor(typeof HTMLCanvasElement === 'undefined' ? null : HTMLCanvasElement.prototype, 'height').get || function() { + throw new Error(`wasm-bindgen: HTMLCanvasElement.height does not exist`); +}; + +__exports.__widl_f_height_HTMLCanvasElement = function(arg0) { + return __widl_f_height_HTMLCanvasElement_target.call(getObject(arg0)); +}; + +__exports.__widl_instanceof_WebGLRenderingContext = function(idx) { + return getObject(idx) instanceof WebGLRenderingContext ? 1 : 0; +}; + +const __widl_f_buffer_data_with_array_buffer_view_WebGLRenderingContext_target = typeof WebGLRenderingContext === 'undefined' ? null : WebGLRenderingContext.prototype.bufferData || function() { + throw new Error(`wasm-bindgen: WebGLRenderingContext.bufferData does not exist`); +}; + +__exports.__widl_f_buffer_data_with_array_buffer_view_WebGLRenderingContext = function(arg0, arg1, arg2, arg3) { + __widl_f_buffer_data_with_array_buffer_view_WebGLRenderingContext_target.call(getObject(arg0), arg1, getObject(arg2), arg3); +}; + +const __widl_f_attach_shader_WebGLRenderingContext_target = typeof WebGLRenderingContext === 'undefined' ? null : WebGLRenderingContext.prototype.attachShader || function() { + throw new Error(`wasm-bindgen: WebGLRenderingContext.attachShader does not exist`); +}; + +__exports.__widl_f_attach_shader_WebGLRenderingContext = function(arg0, arg1, arg2) { + __widl_f_attach_shader_WebGLRenderingContext_target.call(getObject(arg0), getObject(arg1), getObject(arg2)); +}; + +const __widl_f_bind_buffer_WebGLRenderingContext_target = typeof WebGLRenderingContext === 'undefined' ? null : WebGLRenderingContext.prototype.bindBuffer || function() { + throw new Error(`wasm-bindgen: WebGLRenderingContext.bindBuffer does not exist`); +}; + +__exports.__widl_f_bind_buffer_WebGLRenderingContext = function(arg0, arg1, arg2) { + __widl_f_bind_buffer_WebGLRenderingContext_target.call(getObject(arg0), arg1, getObject(arg2)); +}; + +const __widl_f_blend_func_WebGLRenderingContext_target = typeof WebGLRenderingContext === 'undefined' ? null : WebGLRenderingContext.prototype.blendFunc || function() { + throw new Error(`wasm-bindgen: WebGLRenderingContext.blendFunc does not exist`); +}; + +__exports.__widl_f_blend_func_WebGLRenderingContext = function(arg0, arg1, arg2) { + __widl_f_blend_func_WebGLRenderingContext_target.call(getObject(arg0), arg1, arg2); +}; + +const __widl_f_clear_WebGLRenderingContext_target = typeof WebGLRenderingContext === 'undefined' ? null : WebGLRenderingContext.prototype.clear || function() { + throw new Error(`wasm-bindgen: WebGLRenderingContext.clear does not exist`); +}; + +__exports.__widl_f_clear_WebGLRenderingContext = function(arg0, arg1) { + __widl_f_clear_WebGLRenderingContext_target.call(getObject(arg0), arg1); +}; + +const __widl_f_clear_color_WebGLRenderingContext_target = typeof WebGLRenderingContext === 'undefined' ? null : WebGLRenderingContext.prototype.clearColor || function() { + throw new Error(`wasm-bindgen: WebGLRenderingContext.clearColor does not exist`); +}; + +__exports.__widl_f_clear_color_WebGLRenderingContext = function(arg0, arg1, arg2, arg3, arg4) { + __widl_f_clear_color_WebGLRenderingContext_target.call(getObject(arg0), arg1, arg2, arg3, arg4); +}; + +const __widl_f_compile_shader_WebGLRenderingContext_target = typeof WebGLRenderingContext === 'undefined' ? null : WebGLRenderingContext.prototype.compileShader || function() { + throw new Error(`wasm-bindgen: WebGLRenderingContext.compileShader does not exist`); +}; + +__exports.__widl_f_compile_shader_WebGLRenderingContext = function(arg0, arg1) { + __widl_f_compile_shader_WebGLRenderingContext_target.call(getObject(arg0), getObject(arg1)); +}; + +const __widl_f_create_buffer_WebGLRenderingContext_target = typeof WebGLRenderingContext === 'undefined' ? null : WebGLRenderingContext.prototype.createBuffer || function() { + throw new Error(`wasm-bindgen: WebGLRenderingContext.createBuffer does not exist`); +}; + +__exports.__widl_f_create_buffer_WebGLRenderingContext = function(arg0) { + + const val = __widl_f_create_buffer_WebGLRenderingContext_target.call(getObject(arg0)); + return isLikeNone(val) ? 0 : addHeapObject(val); + +}; + +const __widl_f_create_program_WebGLRenderingContext_target = typeof WebGLRenderingContext === 'undefined' ? null : WebGLRenderingContext.prototype.createProgram || function() { + throw new Error(`wasm-bindgen: WebGLRenderingContext.createProgram does not exist`); +}; + +__exports.__widl_f_create_program_WebGLRenderingContext = function(arg0) { + + const val = __widl_f_create_program_WebGLRenderingContext_target.call(getObject(arg0)); + return isLikeNone(val) ? 0 : addHeapObject(val); + +}; + +const __widl_f_create_shader_WebGLRenderingContext_target = typeof WebGLRenderingContext === 'undefined' ? null : WebGLRenderingContext.prototype.createShader || function() { + throw new Error(`wasm-bindgen: WebGLRenderingContext.createShader does not exist`); +}; + +__exports.__widl_f_create_shader_WebGLRenderingContext = function(arg0, arg1) { + + const val = __widl_f_create_shader_WebGLRenderingContext_target.call(getObject(arg0), arg1); + return isLikeNone(val) ? 0 : addHeapObject(val); + +}; + +const __widl_f_draw_elements_with_i32_WebGLRenderingContext_target = typeof WebGLRenderingContext === 'undefined' ? null : WebGLRenderingContext.prototype.drawElements || function() { + throw new Error(`wasm-bindgen: WebGLRenderingContext.drawElements does not exist`); +}; + +__exports.__widl_f_draw_elements_with_i32_WebGLRenderingContext = function(arg0, arg1, arg2, arg3, arg4) { + __widl_f_draw_elements_with_i32_WebGLRenderingContext_target.call(getObject(arg0), arg1, arg2, arg3, arg4); +}; + +const __widl_f_enable_WebGLRenderingContext_target = typeof WebGLRenderingContext === 'undefined' ? null : WebGLRenderingContext.prototype.enable || function() { + throw new Error(`wasm-bindgen: WebGLRenderingContext.enable does not exist`); +}; + +__exports.__widl_f_enable_WebGLRenderingContext = function(arg0, arg1) { + __widl_f_enable_WebGLRenderingContext_target.call(getObject(arg0), arg1); +}; + +const __widl_f_enable_vertex_attrib_array_WebGLRenderingContext_target = typeof WebGLRenderingContext === 'undefined' ? null : WebGLRenderingContext.prototype.enableVertexAttribArray || function() { + throw new Error(`wasm-bindgen: WebGLRenderingContext.enableVertexAttribArray does not exist`); +}; + +__exports.__widl_f_enable_vertex_attrib_array_WebGLRenderingContext = function(arg0, arg1) { + __widl_f_enable_vertex_attrib_array_WebGLRenderingContext_target.call(getObject(arg0), arg1); +}; + +const __widl_f_get_attrib_location_WebGLRenderingContext_target = typeof WebGLRenderingContext === 'undefined' ? null : WebGLRenderingContext.prototype.getAttribLocation || function() { + throw new Error(`wasm-bindgen: WebGLRenderingContext.getAttribLocation does not exist`); +}; + +__exports.__widl_f_get_attrib_location_WebGLRenderingContext = function(arg0, arg1, arg2, arg3) { + let varg2 = getStringFromWasm(arg2, arg3); + return __widl_f_get_attrib_location_WebGLRenderingContext_target.call(getObject(arg0), getObject(arg1), varg2); +}; + +const __widl_f_get_program_info_log_WebGLRenderingContext_target = typeof WebGLRenderingContext === 'undefined' ? null : WebGLRenderingContext.prototype.getProgramInfoLog || function() { + throw new Error(`wasm-bindgen: WebGLRenderingContext.getProgramInfoLog does not exist`); +}; + +__exports.__widl_f_get_program_info_log_WebGLRenderingContext = function(ret, arg0, arg1) { + const val = __widl_f_get_program_info_log_WebGLRenderingContext_target.call(getObject(arg0), getObject(arg1)); + const retptr = isLikeNone(val) ? [0, 0] : passStringToWasm(val); + const retlen = WASM_VECTOR_LEN; + const mem = getUint32Memory(); + mem[ret / 4] = retptr; + mem[ret / 4 + 1] = retlen; + +}; + +const __widl_f_get_program_parameter_WebGLRenderingContext_target = typeof WebGLRenderingContext === 'undefined' ? null : WebGLRenderingContext.prototype.getProgramParameter || function() { + throw new Error(`wasm-bindgen: WebGLRenderingContext.getProgramParameter does not exist`); +}; + +__exports.__widl_f_get_program_parameter_WebGLRenderingContext = function(arg0, arg1, arg2) { + return addHeapObject(__widl_f_get_program_parameter_WebGLRenderingContext_target.call(getObject(arg0), getObject(arg1), arg2)); +}; + +const __widl_f_get_shader_info_log_WebGLRenderingContext_target = typeof WebGLRenderingContext === 'undefined' ? null : WebGLRenderingContext.prototype.getShaderInfoLog || function() { + throw new Error(`wasm-bindgen: WebGLRenderingContext.getShaderInfoLog does not exist`); +}; + +__exports.__widl_f_get_shader_info_log_WebGLRenderingContext = function(ret, arg0, arg1) { + const val = __widl_f_get_shader_info_log_WebGLRenderingContext_target.call(getObject(arg0), getObject(arg1)); + const retptr = isLikeNone(val) ? [0, 0] : passStringToWasm(val); + const retlen = WASM_VECTOR_LEN; + const mem = getUint32Memory(); + mem[ret / 4] = retptr; + mem[ret / 4 + 1] = retlen; + +}; + +const __widl_f_get_shader_parameter_WebGLRenderingContext_target = typeof WebGLRenderingContext === 'undefined' ? null : WebGLRenderingContext.prototype.getShaderParameter || function() { + throw new Error(`wasm-bindgen: WebGLRenderingContext.getShaderParameter does not exist`); +}; + +__exports.__widl_f_get_shader_parameter_WebGLRenderingContext = function(arg0, arg1, arg2) { + return addHeapObject(__widl_f_get_shader_parameter_WebGLRenderingContext_target.call(getObject(arg0), getObject(arg1), arg2)); +}; + +const __widl_f_get_uniform_location_WebGLRenderingContext_target = typeof WebGLRenderingContext === 'undefined' ? null : WebGLRenderingContext.prototype.getUniformLocation || function() { + throw new Error(`wasm-bindgen: WebGLRenderingContext.getUniformLocation does not exist`); +}; + +__exports.__widl_f_get_uniform_location_WebGLRenderingContext = function(arg0, arg1, arg2, arg3) { + let varg2 = getStringFromWasm(arg2, arg3); + + const val = __widl_f_get_uniform_location_WebGLRenderingContext_target.call(getObject(arg0), getObject(arg1), varg2); + return isLikeNone(val) ? 0 : addHeapObject(val); + +}; + +const __widl_f_link_program_WebGLRenderingContext_target = typeof WebGLRenderingContext === 'undefined' ? null : WebGLRenderingContext.prototype.linkProgram || function() { + throw new Error(`wasm-bindgen: WebGLRenderingContext.linkProgram does not exist`); +}; + +__exports.__widl_f_link_program_WebGLRenderingContext = function(arg0, arg1) { + __widl_f_link_program_WebGLRenderingContext_target.call(getObject(arg0), getObject(arg1)); +}; + +const __widl_f_shader_source_WebGLRenderingContext_target = typeof WebGLRenderingContext === 'undefined' ? null : WebGLRenderingContext.prototype.shaderSource || function() { + throw new Error(`wasm-bindgen: WebGLRenderingContext.shaderSource does not exist`); +}; + +__exports.__widl_f_shader_source_WebGLRenderingContext = function(arg0, arg1, arg2, arg3) { + let varg2 = getStringFromWasm(arg2, arg3); + __widl_f_shader_source_WebGLRenderingContext_target.call(getObject(arg0), getObject(arg1), varg2); +}; + +const __widl_f_uniform2f_WebGLRenderingContext_target = typeof WebGLRenderingContext === 'undefined' ? null : WebGLRenderingContext.prototype.uniform2f || function() { + throw new Error(`wasm-bindgen: WebGLRenderingContext.uniform2f does not exist`); +}; + +__exports.__widl_f_uniform2f_WebGLRenderingContext = function(arg0, arg1, arg2, arg3) { + __widl_f_uniform2f_WebGLRenderingContext_target.call(getObject(arg0), getObject(arg1), arg2, arg3); +}; + +const __widl_f_use_program_WebGLRenderingContext_target = typeof WebGLRenderingContext === 'undefined' ? null : WebGLRenderingContext.prototype.useProgram || function() { + throw new Error(`wasm-bindgen: WebGLRenderingContext.useProgram does not exist`); +}; + +__exports.__widl_f_use_program_WebGLRenderingContext = function(arg0, arg1) { + __widl_f_use_program_WebGLRenderingContext_target.call(getObject(arg0), getObject(arg1)); +}; + +const __widl_f_vertex_attrib_pointer_with_i32_WebGLRenderingContext_target = typeof WebGLRenderingContext === 'undefined' ? null : WebGLRenderingContext.prototype.vertexAttribPointer || function() { + throw new Error(`wasm-bindgen: WebGLRenderingContext.vertexAttribPointer does not exist`); +}; + +__exports.__widl_f_vertex_attrib_pointer_with_i32_WebGLRenderingContext = function(arg0, arg1, arg2, arg3, arg4, arg5, arg6) { + __widl_f_vertex_attrib_pointer_with_i32_WebGLRenderingContext_target.call(getObject(arg0), arg1, arg2, arg3, arg4 !== 0, arg5, arg6); +}; + +__exports.__widl_instanceof_Window = function(idx) { + return getObject(idx) instanceof Window ? 1 : 0; +}; + +__exports.__widl_f_document_Window = function(arg0) { + + const val = getObject(arg0).document; + return isLikeNone(val) ? 0 : addHeapObject(val); + +}; + +__exports.__wbg_new_c1b585153cd441ad = function(arg0) { + return addHeapObject(new Float32Array(getObject(arg0))); +}; + +__exports.__wbg_subarray_6bef35518c0617bd = function(arg0, arg1, arg2) { + return addHeapObject(getObject(arg0).subarray(arg1, arg2)); +}; + +__exports.__wbg_newnoargs_6a80f84471205fc8 = function(arg0, arg1) { + let varg0 = getStringFromWasm(arg0, arg1); + return addHeapObject(new Function(varg0)); +}; + +__exports.__wbg_call_582b20dfcad7fee4 = function(arg0, arg1, exnptr) { + try { + return addHeapObject(getObject(arg0).call(getObject(arg1))); + } catch (e) { + const view = getUint32Memory(); + view[exnptr / 4] = 1; + view[exnptr / 4 + 1] = addHeapObject(e); + + } +}; + +__exports.__wbg_new_b192e0932f97e870 = function(arg0) { + return addHeapObject(new Int16Array(getObject(arg0))); +}; + +__exports.__wbg_subarray_46088290ce2ce733 = function(arg0, arg1, arg2) { + return addHeapObject(getObject(arg0).subarray(arg1, arg2)); +}; + +__exports.__wbg_new_bc68e7aae4d4f791 = function(arg0) { + return addHeapObject(new Uint8Array(getObject(arg0))); +}; + +__exports.__wbg_subarray_705096b76e15e94e = function(arg0, arg1, arg2) { + return addHeapObject(getObject(arg0).subarray(arg1, arg2)); +}; + +__exports.__wbg_instanceof_Memory_d223615e29613829 = function(idx) { + return getObject(idx) instanceof WebAssembly.Memory ? 1 : 0; +}; + +__exports.__wbg_buffer_0413d5edaa0ff323 = function(arg0) { + return addHeapObject(getObject(arg0).buffer); +}; + +function freePainter(ptr) { + + wasm.__wbg_painter_free(ptr); +} +/** +*/ +class Painter { + + static __wrap(ptr) { + const obj = Object.create(Painter.prototype); + obj.ptr = ptr; + + return obj; + } + + free() { + const ptr = this.ptr; + this.ptr = 0; + freePainter(ptr); + } + +} +__exports.Painter = Painter; + +__exports.__wbindgen_object_clone_ref = function(idx) { + return addHeapObject(getObject(idx)); +}; + +function dropObject(idx) { + if (idx < 36) return; + heap[idx] = heap_next; + heap_next = idx; +} + +__exports.__wbindgen_object_drop_ref = function(i) { dropObject(i); }; + +__exports.__wbindgen_string_new = function(p, l) { + return addHeapObject(getStringFromWasm(p, l)); +}; + +__exports.__wbindgen_boolean_get = function(i) { + let v = getObject(i); + if (typeof(v) === 'boolean') { + return v ? 1 : 0; + } else { + return 2; + } +}; + +__exports.__wbindgen_memory = function() { return addHeapObject(wasm.memory); }; + +function takeObject(idx) { + const ret = getObject(idx); + dropObject(idx); + return ret; +} + +__exports.__wbindgen_rethrow = function(idx) { throw takeObject(idx); }; + +__exports.__wbindgen_throw = function(ptr, len) { + throw new Error(getStringFromWasm(ptr, len)); +}; + +function init(path_or_module) { + let instantiation; + const imports = { './emgui_wasm': __exports }; + if (path_or_module instanceof WebAssembly.Module) { + instantiation = WebAssembly.instantiate(path_or_module, imports) + .then(instance => { + return { instance, module: path_or_module } + }); +} else { + const data = fetch(path_or_module); + if (typeof WebAssembly.instantiateStreaming === 'function') { + instantiation = WebAssembly.instantiateStreaming(data, imports); + } else { + instantiation = data + .then(response => response.arrayBuffer()) + .then(buffer => WebAssembly.instantiate(buffer, imports)); + } +} +return instantiation.then(({instance}) => { + wasm = init.wasm = instance.exports; + +}); +}; +self.wasm_bindgen = Object.assign(init, __exports); +})(); diff --git a/docs/emgui_wasm_bg.wasm b/docs/emgui_wasm_bg.wasm new file mode 100644 index 00000000..91399a39 Binary files /dev/null and b/docs/emgui_wasm_bg.wasm differ diff --git a/docs/frontend.js b/docs/frontend.js index 95b35564..690b0a3c 100644 --- a/docs/frontend.js +++ b/docs/frontend.js @@ -1,4 +1,6 @@ -function styleFromColor(color) { +// ---------------------------------------------------------------------------- +// Canvas painting: +function style_from_color(color) { return "rgba(" + color.r + ", " + color.g + ", " + color.b + ", " + color.a / 255.0 + ")"; } function paint_command(canvas, cmd) { @@ -9,17 +11,17 @@ function paint_command(canvas, cmd) { ctx.beginPath(); ctx.arc(cmd.center.x, cmd.center.y, cmd.radius, 0, 2 * Math.PI, false); if (cmd.fill_color) { - ctx.fillStyle = styleFromColor(cmd.fill_color); + ctx.fillStyle = style_from_color(cmd.fill_color); ctx.fill(); } if (cmd.outline) { ctx.lineWidth = cmd.outline.width; - ctx.strokeStyle = styleFromColor(cmd.outline.color); + ctx.strokeStyle = style_from_color(cmd.outline.color); ctx.stroke(); } return; case "clear": - ctx.fillStyle = styleFromColor(cmd.fill_color); + ctx.fillStyle = style_from_color(cmd.fill_color); ctx.clearRect(0, 0, canvas.width, canvas.height); return; case "line": @@ -30,7 +32,7 @@ function paint_command(canvas, cmd) { ctx.lineTo(point.x, point.y); } ctx.lineWidth = cmd.width; - ctx.strokeStyle = styleFromColor(cmd.color); + ctx.strokeStyle = style_from_color(cmd.color); ctx.stroke(); return; case "rect": @@ -51,17 +53,17 @@ function paint_command(canvas, cmd) { ctx.quadraticCurveTo(x, y, x + r, y); ctx.closePath(); if (cmd.fill_color) { - ctx.fillStyle = styleFromColor(cmd.fill_color); + ctx.fillStyle = style_from_color(cmd.fill_color); ctx.fill(); } if (cmd.outline) { ctx.lineWidth = cmd.outline.width; - ctx.strokeStyle = styleFromColor(cmd.outline.color); + ctx.strokeStyle = style_from_color(cmd.outline.color); ctx.stroke(); } return; case "text": - ctx.fillStyle = styleFromColor(cmd.fill_color); + ctx.fillStyle = style_from_color(cmd.fill_color); ctx.font = cmd.font_size + "px " + cmd.font_name; ctx.textBaseline = "middle"; ctx.fillText(cmd.text, cmd.pos.x, cmd.pos.y); @@ -75,7 +77,7 @@ function wasm_loaded() { } // here we tell bindgen the path to the wasm file so it can start // initialization and return to us a promise when it's done -wasm_bindgen("./emgui_bg.wasm") +wasm_bindgen("./emgui_wasm_bg.wasm") .then(wasm_loaded)["catch"](console.error); function rust_gui(input) { return JSON.parse(wasm_bindgen.show_gui(JSON.stringify(input))); @@ -96,27 +98,45 @@ function js_gui(input) { }); return commands; } +var WEB_GL = true; +var g_webgl_painter = null; function paint_gui(canvas, input) { - var commands = rust_gui(input); - commands.unshift({ - fill_color: { r: 0, g: 0, b: 0, a: 0 }, - kind: "clear" - }); - for (var _i = 0, commands_1 = commands; _i < commands_1.length; _i++) { - var cmd = commands_1[_i]; - paint_command(canvas, cmd); + if (WEB_GL) { + if (g_webgl_painter === null) { + g_webgl_painter = wasm_bindgen.new_webgl_painter("canvas"); + } + wasm_bindgen.paint_webgl(g_webgl_painter, JSON.stringify(input)); + } + else { + var commands = rust_gui(input); + for (var _i = 0, commands_1 = commands; _i < commands_1.length; _i++) { + var cmd = commands_1[_i]; + var commands_2 = rust_gui(input); + commands_2.unshift({ + fill_color: { r: 0, g: 0, b: 0, a: 0 }, + kind: "clear" + }); + paint_command(canvas, cmd); + } } } // ---------------------------------------------------------------------------- var g_mouse_pos = { x: -1000.0, y: -1000.0 }; var g_mouse_down = false; +function auto_resize_canvas(canvas) { + if (WEB_GL) { + canvas.setAttribute("width", window.innerWidth); + canvas.setAttribute("height", window.innerHeight); + } + else { + var pixels_per_point = window.devicePixelRatio || 1; + var ctx = canvas.getContext("2d"); + ctx.scale(pixels_per_point, pixels_per_point); + canvas.setAttribute("width", window.innerWidth * pixels_per_point); + canvas.setAttribute("height", window.innerHeight * pixels_per_point); + } +} function get_input(canvas) { - var pixels_per_point = window.devicePixelRatio || 1; - var ctx = canvas.getContext("2d"); - ctx.scale(pixels_per_point, pixels_per_point); - // Resize based on screen size: - canvas.setAttribute("width", window.innerWidth * pixels_per_point); - canvas.setAttribute("height", window.innerHeight * pixels_per_point); return { mouse_down: g_mouse_down, mouse_pos: g_mouse_pos, @@ -133,6 +153,7 @@ function mouse_pos_from_event(canvas, event) { function initialize() { console.log("window.devicePixelRatio: " + window.devicePixelRatio); var canvas = document.getElementById("canvas"); + auto_resize_canvas(canvas); var repaint = function () { return paint_gui(canvas, get_input(canvas)); }; canvas.addEventListener("mousemove", function (event) { g_mouse_pos = mouse_pos_from_event(canvas, event); diff --git a/docs/frontend.ts b/docs/frontend.ts index 75f3e268..9213b2c4 100644 --- a/docs/frontend.ts +++ b/docs/frontend.ts @@ -60,7 +60,10 @@ interface Text { type PaintCmd = Circle | Clear | Line | Rect | Text; -function styleFromColor(color: Color): string { +// ---------------------------------------------------------------------------- +// Canvas painting: + +function style_from_color(color: Color): string { return `rgba(${color.r}, ${color.g}, ${color.b}, ${color.a / 255.0})`; } @@ -74,18 +77,18 @@ function paint_command(canvas, cmd: PaintCmd) { ctx.beginPath(); ctx.arc(cmd.center.x, cmd.center.y, cmd.radius, 0, 2 * Math.PI, false); if (cmd.fill_color) { - ctx.fillStyle = styleFromColor(cmd.fill_color); + ctx.fillStyle = style_from_color(cmd.fill_color); ctx.fill(); } if (cmd.outline) { ctx.lineWidth = cmd.outline.width; - ctx.strokeStyle = styleFromColor(cmd.outline.color); + ctx.strokeStyle = style_from_color(cmd.outline.color); ctx.stroke(); } return; case "clear": - ctx.fillStyle = styleFromColor(cmd.fill_color); + ctx.fillStyle = style_from_color(cmd.fill_color); ctx.clearRect(0, 0, canvas.width, canvas.height); return; @@ -96,7 +99,7 @@ function paint_command(canvas, cmd: PaintCmd) { ctx.lineTo(point.x, point.y); } ctx.lineWidth = cmd.width; - ctx.strokeStyle = styleFromColor(cmd.color); + ctx.strokeStyle = style_from_color(cmd.color); ctx.stroke(); return; @@ -118,18 +121,18 @@ function paint_command(canvas, cmd: PaintCmd) { ctx.quadraticCurveTo(x, y, x + r, y); ctx.closePath(); if (cmd.fill_color) { - ctx.fillStyle = styleFromColor(cmd.fill_color); + ctx.fillStyle = style_from_color(cmd.fill_color); ctx.fill(); } if (cmd.outline) { ctx.lineWidth = cmd.outline.width; - ctx.strokeStyle = styleFromColor(cmd.outline.color); + ctx.strokeStyle = style_from_color(cmd.outline.color); ctx.stroke(); } return; case "text": - ctx.fillStyle = styleFromColor(cmd.fill_color); + ctx.fillStyle = style_from_color(cmd.fill_color); ctx.font = `${cmd.font_size}px ${cmd.font_name}`; ctx.textBaseline = "middle"; ctx.fillText(cmd.text, cmd.pos.x, cmd.pos.y); @@ -164,7 +167,7 @@ function wasm_loaded() { // here we tell bindgen the path to the wasm file so it can start // initialization and return to us a promise when it's done -wasm_bindgen("./emgui_bg.wasm") +wasm_bindgen("./emgui_wasm_bg.wasm") .then(wasm_loaded) .catch(console.error); @@ -193,15 +196,26 @@ function js_gui(input: RawInput): PaintCmd[] { return commands; } -function paint_gui(canvas, input: RawInput) { - const commands = rust_gui(input); - commands.unshift({ - fill_color: {r: 0, g: 0, b: 0, a: 0}, - kind: "clear", - }); +const WEB_GL = true; +let g_webgl_painter = null; - for (const cmd of commands) { - paint_command(canvas, cmd); +function paint_gui(canvas, input: RawInput) { + if (WEB_GL) { + if (g_webgl_painter === null) { + g_webgl_painter = wasm_bindgen.new_webgl_painter("canvas"); + } + wasm_bindgen.paint_webgl(g_webgl_painter, JSON.stringify(input)); + } else { + let commands = rust_gui(input); + for (const cmd of commands) { + const commands = rust_gui(input); + commands.unshift({ + fill_color: { r: 0, g: 0, b: 0, a: 0 }, + kind: "clear", + }); + + paint_command(canvas, cmd); + } } } @@ -210,16 +224,22 @@ function paint_gui(canvas, input: RawInput) { let g_mouse_pos = { x: -1000.0, y: -1000.0 }; let g_mouse_down = false; +function auto_resize_canvas(canvas) { + if (WEB_GL) { + canvas.setAttribute("width", window.innerWidth); + canvas.setAttribute("height", window.innerHeight); + } else { + const pixels_per_point = window.devicePixelRatio || 1; + + const ctx = canvas.getContext("2d"); + ctx.scale(pixels_per_point, pixels_per_point); + + canvas.setAttribute("width", window.innerWidth * pixels_per_point); + canvas.setAttribute("height", window.innerHeight * pixels_per_point); + } +} + function get_input(canvas): RawInput { - const pixels_per_point = window.devicePixelRatio || 1; - - const ctx = canvas.getContext("2d"); - ctx.scale(pixels_per_point, pixels_per_point); - - // Resize based on screen size: - canvas.setAttribute("width", window.innerWidth * pixels_per_point); - canvas.setAttribute("height", window.innerHeight * pixels_per_point); - return { mouse_down: g_mouse_down, mouse_pos: g_mouse_pos, @@ -239,49 +259,38 @@ function initialize() { console.log(`window.devicePixelRatio: ${window.devicePixelRatio}`); const canvas = document.getElementById("canvas"); + auto_resize_canvas(canvas); const repaint = () => paint_gui(canvas, get_input(canvas)); - canvas.addEventListener( - "mousemove", - (event) => { - g_mouse_pos = mouse_pos_from_event(canvas, event); - repaint(); - event.stopPropagation(); - event.preventDefault(); - }, - ); + canvas.addEventListener("mousemove", event => { + g_mouse_pos = mouse_pos_from_event(canvas, event); + repaint(); + event.stopPropagation(); + event.preventDefault(); + }); - canvas.addEventListener( - "mouseleave", - (event) => { - g_mouse_pos = { x: -1000.0, y: -1000.0 }; - repaint(); - event.stopPropagation(); - event.preventDefault(); - }, - ); + canvas.addEventListener("mouseleave", event => { + g_mouse_pos = { x: -1000.0, y: -1000.0 }; + repaint(); + event.stopPropagation(); + event.preventDefault(); + }); - canvas.addEventListener( - "mousedown", - (event) => { - g_mouse_pos = mouse_pos_from_event(canvas, event); - g_mouse_down = true; - repaint(); - event.stopPropagation(); - event.preventDefault(); - }, - ); + canvas.addEventListener("mousedown", event => { + g_mouse_pos = mouse_pos_from_event(canvas, event); + g_mouse_down = true; + repaint(); + event.stopPropagation(); + event.preventDefault(); + }); - canvas.addEventListener( - "mouseup", - (event) => { - g_mouse_pos = mouse_pos_from_event(canvas, event); - g_mouse_down = false; - repaint(); - event.stopPropagation(); - event.preventDefault(); - }, - ); + canvas.addEventListener("mouseup", event => { + g_mouse_pos = mouse_pos_from_event(canvas, event); + g_mouse_down = false; + repaint(); + event.stopPropagation(); + event.preventDefault(); + }); window.addEventListener("load", repaint); window.addEventListener("pagehide", repaint); diff --git a/docs/index.html b/docs/index.html index 31720ec9..27cc38ec 100644 --- a/docs/index.html +++ b/docs/index.html @@ -32,7 +32,7 @@ - + diff --git a/emgui/Cargo.toml b/emgui/Cargo.toml index 6be652f4..d0de91a9 100644 --- a/emgui/Cargo.toml +++ b/emgui/Cargo.toml @@ -8,5 +8,6 @@ edition = "2018" [dependencies] # palette = "0.4" +rusttype = "0.7" serde = "1" serde_derive = "1" diff --git a/emgui/fonts/DejaVuSansMono.ttf b/emgui/fonts/DejaVuSansMono.ttf new file mode 100644 index 00000000..f5786022 Binary files /dev/null and b/emgui/fonts/DejaVuSansMono.ttf differ diff --git a/emgui/fonts/ProggyClean.ttf b/emgui/fonts/ProggyClean.ttf new file mode 100644 index 00000000..0270cdfe Binary files /dev/null and b/emgui/fonts/ProggyClean.ttf differ diff --git a/emgui/fonts/Roboto-Regular.ttf b/emgui/fonts/Roboto-Regular.ttf new file mode 100644 index 00000000..2c97eead Binary files /dev/null and b/emgui/fonts/Roboto-Regular.ttf differ diff --git a/emgui/src/font.rs b/emgui/src/font.rs new file mode 100644 index 00000000..cf7a49ab --- /dev/null +++ b/emgui/src/font.rs @@ -0,0 +1,218 @@ +#![allow(unused)] // TODO + +use rusttype::{point, Scale}; + +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct GlyphInfo { + /// X offset for nice rendering + offset_x: u16, + + /// Y offset for nice rendering + offset_y: u16, + + min_x: u16, + min_y: u16, + + /// Inclusive. + max_x: u16, + + /// Inclusive + max_y: u16, +} + +/// Printable ascii characters [33, 126], which excludes 32 (space) and 127 (DEL) +const NUM_CHARS: usize = 94; +const FIRST_ASCII: usize = 33; +/// Inclusive +const LAST_ASCII: usize = 126; + +#[derive(Clone)] +pub struct Font { + /// Maximum character height + scale: usize, + /// NUM_CHARS big + char_rects: Vec, + atlas_width: usize, + atlas_height: usize, + atlas: Vec, +} + +impl Font { + pub fn new(scale: usize) -> Font { + let font_data = include_bytes!("../fonts/ProggyClean.ttf"); + let font = rusttype::Font::from_bytes(font_data as &[u8]).expect("Error constructing Font"); + + // println!( + // "font.v_metrics: {:?}", + // font.v_metrics(Scale::uniform(scale as f32)) + // ); + + let glyphs: Vec<_> = Self::supported_characters() + .map(|c| { + let glyph = font.glyph(c); + assert_ne!( + glyph.id().0, + 0, + "Failed to find a glyph for the character '{}'", + c + ); + let glyph = glyph.scaled(Scale::uniform(scale as f32)); + glyph.positioned(point(0.0, 0.0)) + }) + .collect(); + + // TODO: decide dynamically? + let atlas_width = 128; + + let mut atlas_height = 8; + let mut atlas = vec![0; atlas_width * atlas_height]; + + // Make one white pixel for use for various stuff: + atlas[0] = 255; + + let mut cursor_x = 1; + let mut cursor_y = 0; + let mut row_height = 1; + + let mut char_rects = vec![]; + + for glyph in glyphs { + let bb = glyph + .pixel_bounding_box() + .expect("Failed to get pixel bounding box"); + let glyph_width = bb.width() as usize; + let glyph_height = bb.height() as usize; + assert!(glyph_width >= 1); + assert!(glyph_height >= 1); + assert!(glyph_width <= atlas_width); + if cursor_x + glyph_width > atlas_width { + // New row: + cursor_x = 0; + cursor_y += row_height; + row_height = 0; + } + + row_height = row_height.max(glyph_height); + while cursor_y + row_height >= atlas_height { + atlas_height *= 2; + } + if atlas_width * atlas_height > atlas.len() { + atlas.resize(atlas_width * atlas_height, 0); + } + + glyph.draw(|x, y, v| { + if v > 0.0 { + let x = x as usize; + let y = y as usize; + let px = cursor_x + x as usize; + let py = cursor_y + y as usize; + atlas[py * atlas_width + px] = (v * 255.0).round() as u8; + } + }); + + let offset_y = scale as i32 + bb.min.y - 3; // TODO: use font.v_metrics + assert!(0 <= bb.min.x); + assert!(0 <= offset_y && offset_y < scale as i32); + char_rects.push(GlyphInfo { + offset_x: bb.min.x as u16, + offset_y: offset_y as u16, + min_x: cursor_x as u16, + min_y: cursor_y as u16, + max_x: (cursor_x + glyph_width - 1) as u16, + max_y: (cursor_y + glyph_height - 1) as u16, + }); + + cursor_x += glyph_width; + } + + Font { + scale, + char_rects, + atlas_width, + atlas_height, + atlas, + } + } + + pub fn supported_characters() -> impl Iterator { + (FIRST_ASCII..=LAST_ASCII).map(|c| c as u8 as char) + } + + pub fn texture(&self) -> (usize, usize, &[u8]) { + (self.atlas_width, self.atlas_height, &self.atlas) + } + + pub fn pixel(&self, x: u16, y: u16) -> u8 { + let x = x as usize; + let y = y as usize; + assert!(x < self.atlas_width); + assert!(y < self.atlas_height); + self.atlas[y * self.atlas_width + x] + } + + pub fn glyph_info(&self, c: char) -> Option { + let c = c as usize; + if FIRST_ASCII <= c && c <= LAST_ASCII { + Some(self.char_rects[c - FIRST_ASCII]) + } else { + None + } + } + + pub fn debug_print_atlas_ascii_art(&self) { + for y in 0..self.atlas_height { + println!( + "{}", + as_ascii(&self.atlas[y * self.atlas_width..(y + 1) * self.atlas_width]) + ); + } + } + + pub fn debug_print_all_chars(&self) { + let max_width = 160; + let mut pixel_rows = vec![vec![0; max_width]; self.scale]; + let mut cursor_x = 0; + let mut cursor_y = 0; + for c in Self::supported_characters() { + if let Some(glyph_info) = self.glyph_info(c) { + for x in glyph_info.min_x..=glyph_info.max_x { + for y in glyph_info.min_y..=glyph_info.max_y { + let pixel = self.pixel(x, y); + let rx = glyph_info.offset_x + x - glyph_info.min_x; + let ry = glyph_info.offset_y + y - glyph_info.min_y; + pixel_rows[cursor_y + ry as usize][cursor_x + rx as usize] = pixel; + } + } + cursor_x += 7; // TODO + if cursor_x + 7 >= max_width { + println!("{}", (0..max_width).map(|_| "X").collect::()); + for row in pixel_rows { + println!("{}", as_ascii(&row)); + } + pixel_rows = vec![vec![0; max_width]; self.scale]; + cursor_x = 0; + } + } + } + println!("{}", (0..max_width).map(|_| "X").collect::()); + } +} + +fn as_ascii(pixels: &[u8]) -> String { + pixels + .iter() + .map(|pixel| if *pixel == 0 { ' ' } else { 'X' }) + .collect() +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn font_test() { + let font = Font::new(13); + font.debug_print_atlas_ascii_art(); + font.debug_print_all_chars(); + panic!(); + } +} diff --git a/emgui/src/lib.rs b/emgui/src/lib.rs index a8996947..349353b6 100644 --- a/emgui/src/lib.rs +++ b/emgui/src/lib.rs @@ -1,16 +1,24 @@ #![deny(warnings)] +extern crate rusttype; extern crate serde; #[macro_use] // TODO: get rid of this extern crate serde_derive; mod emgui; +mod font; mod layout; pub mod math; +mod painter; mod style; pub mod types; pub use crate::{ - emgui::Emgui, layout::Layout, layout::LayoutOptions, style::Style, types::RawInput, + emgui::Emgui, + layout::Layout, + layout::LayoutOptions, + painter::{Frame, Painter, Vertex}, + style::Style, + types::RawInput, }; diff --git a/emgui/src/painter.rs b/emgui/src/painter.rs new file mode 100644 index 00000000..76df9aae --- /dev/null +++ b/emgui/src/painter.rs @@ -0,0 +1,88 @@ +/// Outputs render info in a format suitable for e.g. OpenGL. +use crate::{ + font::Font, + types::{Color, PaintCmd}, +}; + +#[derive(Clone, Copy, Debug, Default)] +pub struct Vertex { + /// Pixel coordinated + pub x: f32, + /// Pixel coordinated + pub y: f32, + /// Texel indices into the texture + pub u: u16, + /// Texel indices into the texture + pub v: u16, + /// sRGBA + pub color: Color, +} + +#[derive(Clone, Debug, Default)] +pub struct Frame { + pub clear_color: Option, + /// One big triangle strip + pub indices: Vec, + pub vertices: Vec, +} + +#[derive(Clone)] +pub struct Painter { + font: Font, +} + +impl Painter { + pub fn new() -> Painter { + Painter { + font: Font::new(13), + } + } + + /// 8-bit row-major font atlas texture, (width, height, pixels). + pub fn texture(&self) -> (usize, usize, &[u8]) { + self.font.texture() + } + + pub fn paint(&self, commands: &[PaintCmd]) -> Frame { + let mut frame = Frame::default(); + for cmd in commands { + match cmd { + PaintCmd::Circle { .. } => {} // TODO + PaintCmd::Clear { fill_color } => { + frame.clear_color = Some(*fill_color); + } + PaintCmd::Line { .. } => {} // TODO + PaintCmd::Rect { + pos, + size, + fill_color, + .. + } => { + // TODO: rounded corners, colors etc. + let idx = frame.vertices.len() as u32; + frame.indices.push(idx + 0); + frame.indices.push(idx + 0); + frame.indices.push(idx + 1); + frame.indices.push(idx + 2); + frame.indices.push(idx + 3); + frame.indices.push(idx + 3); + + let vert = |x, y| Vertex { + x, + y, + u: 0, + v: 0, + color: fill_color.unwrap_or(Color::WHITE), + }; + + frame.vertices.push(vert(pos.x, pos.y)); + frame.vertices.push(vert(pos.x + size.x, pos.y)); + frame.vertices.push(vert(pos.x, pos.y + size.y)); + frame.vertices.push(vert(pos.x + size.x, pos.y + size.y)); + } + PaintCmd::Text { .. } => {} // TODO + } + } + frame + } +} diff --git a/emgui/src/types.rs b/emgui/src/types.rs index 9ea8a417..276b4588 100644 --- a/emgui/src/types.rs +++ b/emgui/src/types.rs @@ -57,7 +57,11 @@ pub struct Color { pub a: u8, } -pub fn srgba(r: u8, g: u8, b: u8, a: u8) -> Color { +impl Color { + pub const WHITE: Color = srgba(255, 255, 255, 255); +} + +pub const fn srgba(r: u8, g: u8, b: u8, a: u8) -> Color { Color { r, g, b, a } } diff --git a/emgui_wasm/Cargo.toml b/emgui_wasm/Cargo.toml index 5aeed7d5..6425783d 100644 --- a/emgui_wasm/Cargo.toml +++ b/emgui_wasm/Cargo.toml @@ -8,10 +8,24 @@ edition = "2018" crate-type = ["cdylib", "rlib"] [dependencies] +js-sys = "0.3" lazy_static = "1" serde = "1" serde_json = "1" wasm-bindgen = "0.2" -# web-sys = { version = "0.3.5", features = ['console', 'Performance', 'Window'] } emgui = { path = "../emgui" } + +[dependencies.web-sys] +version = "0.3" +features = [ + 'Document', + 'Element', + 'HtmlCanvasElement', + 'WebGlBuffer', + 'WebGlProgram', + 'WebGlRenderingContext', + 'WebGlShader', + 'WebGlUniformLocation', + 'Window', +] diff --git a/emgui_wasm/src/lib.rs b/emgui_wasm/src/lib.rs index cfddc6f1..0e9a544b 100644 --- a/emgui_wasm/src/lib.rs +++ b/emgui_wasm/src/lib.rs @@ -9,11 +9,12 @@ extern crate emgui; use std::sync::Mutex; -use emgui::{Emgui, RawInput}; +use emgui::{Emgui, Frame, RawInput}; use wasm_bindgen::prelude::*; -pub mod app; +mod app; +mod webgl; #[wasm_bindgen] pub fn show_gui(raw_input_json: &str) -> String { @@ -40,3 +41,57 @@ pub fn show_gui(raw_input_json: &str) -> String { let commands = emgui.paint(); serde_json::to_string(&commands).unwrap() } + +#[wasm_bindgen] +pub fn new_webgl_painter(canvas_id: &str) -> Result { + webgl::Painter::new(canvas_id) +} + +struct State { + app: app::App, + emgui: Emgui, + emgui_painter: emgui::Painter, +} + +impl State { + fn new() -> State { + State { + app: Default::default(), + emgui: Default::default(), + emgui_painter: emgui::Painter::new(), + } + } + + fn frame(&mut self, raw_input: RawInput) -> Frame { + self.emgui.new_frame(raw_input); + + use crate::app::GuiSettings; + self.app.show_gui(&mut self.emgui.layout); + + let mut style = self.emgui.style.clone(); + self.emgui.layout.foldable("Style", |gui| { + style.show_gui(gui); + }); + self.emgui.style = style; + + let commands = self.emgui.paint(); + self.emgui_painter.paint(&commands) + } +} + +#[wasm_bindgen] +pub fn paint_webgl(webgl_painter: &webgl::Painter, raw_input_json: &str) -> Result<(), JsValue> { + // TODO: nicer interface than JSON + let raw_input: RawInput = serde_json::from_str(raw_input_json).unwrap(); + + lazy_static::lazy_static! { + static ref STATE: Mutex> = Default::default(); + } + + let mut state = STATE.lock().unwrap(); + if state.is_none() { + *state = Some(State::new()); + } + let frame = state.as_mut().unwrap().frame(raw_input); + webgl_painter.paint(&frame) +} diff --git a/emgui_wasm/src/webgl.rs b/emgui_wasm/src/webgl.rs new file mode 100644 index 00000000..ae8eae04 --- /dev/null +++ b/emgui_wasm/src/webgl.rs @@ -0,0 +1,267 @@ +use { + js_sys::WebAssembly, + wasm_bindgen::{prelude::*, JsCast}, + web_sys::{WebGlBuffer, WebGlProgram, WebGlRenderingContext, WebGlShader}, +}; + +use emgui::Frame; + +#[wasm_bindgen] +pub struct Painter { + canvas: web_sys::HtmlCanvasElement, + gl: WebGlRenderingContext, + program: WebGlProgram, + index_buffer: WebGlBuffer, + pos_buffer: WebGlBuffer, + color_buffer: WebGlBuffer, +} + +impl Painter { + pub fn new(canvas_id: &str) -> Result { + let document = web_sys::window().unwrap().document().unwrap(); + let canvas = document.get_element_by_id(canvas_id).unwrap(); + let canvas: web_sys::HtmlCanvasElement = canvas.dyn_into::()?; + + let gl = canvas + .get_context("webgl")? + .unwrap() + .dyn_into::()?; + + gl.enable(WebGlRenderingContext::BLEND); + gl.blend_func( + WebGlRenderingContext::SRC_ALPHA, + WebGlRenderingContext::ONE_MINUS_SRC_ALPHA, + ); + + let vert_shader = compile_shader( + &gl, + WebGlRenderingContext::VERTEX_SHADER, + r#" + uniform vec2 u_screen_size; + attribute vec2 a_pos; + attribute vec4 a_color; + varying vec4 v_color; + void main() { + gl_Position = vec4( + 2.0 * a_pos.x / u_screen_size.x - 1.0, + 1.0 - 2.0 * a_pos.y / u_screen_size.y, + 0.0, + 1.0); + // v_color = vec4(1.0, 0.0, 0.0, 0.5); + v_color = a_color; + } + "#, + )?; + let frag_shader = compile_shader( + &gl, + WebGlRenderingContext::FRAGMENT_SHADER, + r#" + precision highp float; + varying vec4 v_color; + void main() { + gl_FragColor = v_color; + } + "#, + )?; + let program = link_program(&gl, [vert_shader, frag_shader].iter())?; + let index_buffer = gl.create_buffer().ok_or("failed to create index_buffer")?; + let pos_buffer = gl.create_buffer().ok_or("failed to create pos_buffer")?; + let color_buffer = gl.create_buffer().ok_or("failed to create color_buffer")?; + + Ok(Painter { + canvas, + gl, + program, + index_buffer, + pos_buffer, + color_buffer, + }) + } + + pub fn paint(&self, frame: &Frame) -> Result<(), JsValue> { + let gl = &self.gl; + + // -------------------------------------------------------------------- + + gl.use_program(Some(&self.program)); + + // -------------------------------------------------------------------- + + let indices: Vec = frame.indices.iter().map(|idx| *idx as u16).collect(); + + let mut positions = Vec::with_capacity(2 * frame.vertices.len()); + for v in &frame.vertices { + positions.push(v.x); + positions.push(v.y); + } + + let mut colors = Vec::with_capacity(4 * frame.vertices.len()); + for v in &frame.vertices { + colors.push(v.color.r); + colors.push(v.color.g); + colors.push(v.color.b); + colors.push(v.color.a); + } + + // -------------------------------------------------------------------- + + let indices_memory_buffer = wasm_bindgen::memory() + .dyn_into::()? + .buffer(); + let indices_ptr = indices.as_ptr() as u32 / 2; + let indices_array = js_sys::Int16Array::new(&indices_memory_buffer) + .subarray(indices_ptr, indices_ptr + indices.len() as u32); + + gl.bind_buffer( + WebGlRenderingContext::ELEMENT_ARRAY_BUFFER, + Some(&self.index_buffer), + ); + gl.buffer_data_with_array_buffer_view( + WebGlRenderingContext::ELEMENT_ARRAY_BUFFER, + &indices_array, + WebGlRenderingContext::STATIC_DRAW, // TODO: STREAM ? + ); + + // -------------------------------------------------------------------- + + let pos_memory_buffer = wasm_bindgen::memory() + .dyn_into::()? + .buffer(); + let pos_ptr = positions.as_ptr() as u32 / 4; + let pos_array = js_sys::Float32Array::new(&pos_memory_buffer) + .subarray(pos_ptr, pos_ptr + positions.len() as u32); + + gl.bind_buffer(WebGlRenderingContext::ARRAY_BUFFER, Some(&self.pos_buffer)); + gl.buffer_data_with_array_buffer_view( + WebGlRenderingContext::ARRAY_BUFFER, + &pos_array, + WebGlRenderingContext::STATIC_DRAW, // TODO: STREAM ? + ); + + let a_pos_loc = gl.get_attrib_location(&self.program, "a_pos"); + assert!(a_pos_loc >= 0); + let a_pos_loc = a_pos_loc as u32; + + let normalize = false; + let stride = 0; + let offset = 0; + gl.vertex_attrib_pointer_with_i32( + a_pos_loc, + 2, + WebGlRenderingContext::FLOAT, + normalize, + stride, + offset, + ); + gl.enable_vertex_attrib_array(a_pos_loc); + + // -------------------------------------------------------------------- + + let colors_memory_buffer = wasm_bindgen::memory() + .dyn_into::()? + .buffer(); + let colors_ptr = colors.as_ptr() as u32; + let colors_array = js_sys::Uint8Array::new(&colors_memory_buffer) + .subarray(colors_ptr, colors_ptr + colors.len() as u32); + + gl.bind_buffer( + WebGlRenderingContext::ARRAY_BUFFER, + Some(&self.color_buffer), + ); + gl.buffer_data_with_array_buffer_view( + WebGlRenderingContext::ARRAY_BUFFER, + &colors_array, + WebGlRenderingContext::STATIC_DRAW, // TODO: STREAM ? + ); + + let a_color_loc = gl.get_attrib_location(&self.program, "a_color"); + assert!(a_color_loc >= 0); + let a_color_loc = a_color_loc as u32; + + let normalize = true; + let stride = 0; + let offset = 0; + gl.vertex_attrib_pointer_with_i32( + a_color_loc, + 4, + WebGlRenderingContext::UNSIGNED_BYTE, + normalize, + stride, + offset, + ); + gl.enable_vertex_attrib_array(a_color_loc); + + // -------------------------------------------------------------------- + + let u_screen_size_loc = gl + .get_uniform_location(&self.program, "u_screen_size") + .unwrap(); + gl.uniform2f( + Some(&u_screen_size_loc), + self.canvas.width() as f32, + self.canvas.height() as f32, + ); + // gl.uniform2f(Some(&u_screen_size_loc), 4.0, 1.0); + + gl.clear_color(0.05, 0.05, 0.05, 1.0); + gl.clear(WebGlRenderingContext::COLOR_BUFFER_BIT); + + gl.draw_elements_with_i32( + WebGlRenderingContext::TRIANGLE_STRIP, + indices.len() as i32, + WebGlRenderingContext::UNSIGNED_SHORT, + 0, + ); + + Ok(()) + } +} + +fn compile_shader( + gl: &WebGlRenderingContext, + shader_type: u32, + source: &str, +) -> Result { + let shader = gl + .create_shader(shader_type) + .ok_or_else(|| String::from("Unable to create shader object"))?; + gl.shader_source(&shader, source); + gl.compile_shader(&shader); + + if gl + .get_shader_parameter(&shader, WebGlRenderingContext::COMPILE_STATUS) + .as_bool() + .unwrap_or(false) + { + Ok(shader) + } else { + Err(gl + .get_shader_info_log(&shader) + .unwrap_or_else(|| "Unknown error creating shader".into())) + } +} + +fn link_program<'a, T: IntoIterator>( + gl: &WebGlRenderingContext, + shaders: T, +) -> Result { + let program = gl + .create_program() + .ok_or_else(|| String::from("Unable to create shader object"))?; + for shader in shaders { + gl.attach_shader(&program, shader) + } + gl.link_program(&program); + + if gl + .get_program_parameter(&program, WebGlRenderingContext::LINK_STATUS) + .as_bool() + .unwrap_or(false) + { + Ok(program) + } else { + Err(gl + .get_program_info_log(&program) + .unwrap_or_else(|| "Unknown error creating program object".into())) + } +} diff --git a/tslint.json b/tslint.json index 6983dcaf..1bab9e3f 100644 --- a/tslint.json +++ b/tslint.json @@ -1,10 +1,9 @@ { "defaultSeverity": "error", - "extends": [ - "tslint:recommended" - ], + "extends": ["tslint:recommended"], "jsRules": {}, "rules": { + "arrow-parens": [true, "ban-single-arg-parens"], "interface-name": [true, "never-prefix"], "max-classes-per-file": [false], "no-bitwise": false,