Mercurial > hg > orthanc-stone
changeset 2194:c4c6884b5f56
support for large OsiriX files
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 18 Apr 2025 17:37:07 +0200 |
parents | e7a36e84f808 |
children | fe1ee95aa12c 1c5d4541cfb5 |
files | Applications/StoneWebViewer/WebApplication/app.js Applications/StoneWebViewer/WebAssembly/CMakeLists.txt Applications/StoneWebViewer/WebAssembly/ParseWebAssemblyExports.py Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp |
diffstat | 4 files changed, 69 insertions(+), 7 deletions(-) [+] |
line wrap: on
line diff
--- a/Applications/StoneWebViewer/WebApplication/app.js Fri Apr 18 14:37:14 2025 +0200 +++ b/Applications/StoneWebViewer/WebApplication/app.js Fri Apr 18 17:37:07 2025 +0200 @@ -1778,6 +1778,23 @@ document.onselectstart = new Function ('return false'); + +function CallLoadOsirixAnnotations(xml, clear) { + /** + * XML files from OsiriX can be very large, so it is not possible to + * send them to WebAssembly as a "string" parameter, otherwise it + * may result in a stack overflow. We thus have to copy the + * JavaScript string into the WebAssembly heap. + * https://thewasmfrontier.hashnode.dev/working-with-string-in-webassembly + **/ + var length = Module.lengthBytesUTF8(xml); + var pointer = stone.Allocate(length + 1); + Module.stringToUTF8(xml, pointer, length + 1); + stone.LoadOsiriXAnnotations(pointer, length + 1, clear); + stone.Deallocate(pointer); +} + + window.addEventListener('message', function(e) { if ('type' in e.data) { if (e.data.type == 'show-osirix-annotations') { @@ -1797,12 +1814,21 @@ if ('clear' in e.data) { clear = e.data.clear; } - - app.LoadOsiriXAnnotations(e.data.xml, clear); + + if ('xml' in e.data) { + CallLoadOsirixAnnotations(e.data.xml, clear); + } else if ('url' in e.data) { + axios.get(e.data.url) + .then(function(response) { + CallLoadOsirixAnnotations(response.data, clear); + }); + } else { + console.error('Neither "xml", nor "url" was provided to load OsiriX annotations from'); + } } } else { - console.log('Unknown type of dynamic action in the Stone Web viewer: ' + e.data.type); + console.error('Unknown type of dynamic action in the Stone Web viewer: ' + e.data.type); } } });
--- a/Applications/StoneWebViewer/WebAssembly/CMakeLists.txt Fri Apr 18 14:37:14 2025 +0200 +++ b/Applications/StoneWebViewer/WebAssembly/CMakeLists.txt Fri Apr 18 17:37:07 2025 +0200 @@ -36,7 +36,7 @@ set(WASM_FLAGS "${WASM_FLAGS} -s SAFE_HEAP=1") endif() -set(WASM_LINKER_FLAGS "${WASM_LINKER_FLAGS} -s EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]'") +set(WASM_LINKER_FLAGS "${WASM_LINKER_FLAGS} -s EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\", \"lengthBytesUTF8\", \"stringToUTF8\"]'") set(WASM_LINKER_FLAGS "${WASM_LINKER_FLAGS} -s ERROR_ON_UNDEFINED_SYMBOLS=1") set(WASM_LINKER_FLAGS "${WASM_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1 -s TOTAL_MEMORY=268435456") # 256MB + resize set(WASM_LINKER_FLAGS "${WASM_LINKER_FLAGS} -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1")
--- a/Applications/StoneWebViewer/WebAssembly/ParseWebAssemblyExports.py Fri Apr 18 14:37:14 2025 +0200 +++ b/Applications/StoneWebViewer/WebAssembly/ParseWebAssemblyExports.py Fri Apr 18 17:37:07 2025 +0200 @@ -160,6 +160,9 @@ elif returnType in [ 'int', 'unsigned int' ]: f['hasReturn'] = True f['returnType'] = "'int'" + elif returnType == 'void *': + f['hasReturn'] = True + f['returnType'] = "'int'" else: raise Exception('Unknown return type in function "%s()": %s' % (node.spelling, returnType)) @@ -176,6 +179,10 @@ arg['type'] = "'string'" elif argType == 'double': arg['type'] = "'double'" + elif argType == 'size_t': + arg['type'] = "'int'" + elif argType in [ 'const void *', 'void *' ]: + arg['type'] = "'int'" else: raise Exception('Unknown type for argument "%s" in function "%s()": %s' % (child.displayname, node.spelling, argType))
--- a/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp Fri Apr 18 14:37:14 2025 +0200 +++ b/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp Fri Apr 18 17:37:07 2025 +0200 @@ -4125,6 +4125,15 @@ } + +typedef void (*PluginInitializer) (); + +static const PluginInitializer pluginsInitializers_[] = { + NULL +}; + + + extern "C" { int main(int argc, char const *argv[]) @@ -4143,6 +4152,11 @@ instancesCache_.reset(new InstancesCache); osiriXAnnotations_.reset(new OrthancStone::OsiriX::CollectionOfAnnotations); + for (size_t i = 0; pluginsInitializers_[i] != NULL; i++) + { + pluginsInitializers_[i] (); + } + DISPATCH_JAVASCRIPT_EVENT("StoneInitialized"); } @@ -4727,7 +4741,8 @@ // Side-effect: "GetStringBuffer()" is filled with the "Series // Instance UID" of the first loaded annotation EMSCRIPTEN_KEEPALIVE - int LoadOsiriXAnnotations(const char* xml, + int LoadOsiriXAnnotations(const void* xmlPointer, + int xmlSize, int clearPreviousAnnotations) { try @@ -4736,8 +4751,8 @@ { osiriXAnnotations_->Clear(); } - - osiriXAnnotations_->LoadXml(xml); + + osiriXAnnotations_->LoadXml(reinterpret_cast<const char*>(xmlPointer), xmlSize); // Force redraw, as the annotations might have changed for (Viewports::iterator it = allViewports_.begin(); it != allViewports_.end(); ++it) @@ -4895,4 +4910,18 @@ } EXTERN_CATCH_EXCEPTIONS; } + + + EMSCRIPTEN_KEEPALIVE + void *Allocate(size_t size) + { + return malloc(size); + } + + + EMSCRIPTEN_KEEPALIVE + void Deallocate(void* ptr) + { + free(ptr); + } }