# HG changeset patch # User am@osimis.io # Date 1528975720 -7200 # Node ID c8f11437a6fd738d0b340e67b8bc96b5aaa07e7e # Parent 1fa4c65c7e1b5816476e4bd8bf8b8b18e5c7fd1a getting ready for multiple viewports diff -r 1fa4c65c7e1b -r c8f11437a6fd Platforms/WebAssembly/Defaults.cpp --- a/Platforms/WebAssembly/Defaults.cpp Thu Jun 14 12:32:08 2018 +0200 +++ b/Platforms/WebAssembly/Defaults.cpp Thu Jun 14 13:28:40 2018 +0200 @@ -17,19 +17,43 @@ static OrthancStone::StatusBar statusBar_; +static std::list> viewports_; + #ifdef __cplusplus extern "C" { #endif using namespace OrthancStone; - - void EMSCRIPTEN_KEEPALIVE CreateWasmApplication() { + + // when WASM needs a C++ viewport + ViewportHandle EMSCRIPTEN_KEEPALIVE CreateCppViewport() { + + std::shared_ptr viewport(new OrthancStone::WidgetViewport); + printf("viewport %x\n", viewport.get()); + + viewports_.push_back(viewport); + + printf("There are now %d viewports in C++\n", viewports_.size()); + + viewport->SetStatusBar(statusBar_); + viewport->Register(changeObserver_); + + // TODO: remove once we really want to handle multiple viewports + viewport_ = viewport; + return viewport.get(); + } + + // when WASM does not need a viewport anymore, it should release it + void EMSCRIPTEN_KEEPALIVE ReleaseCppViewport(ViewportHandle viewport) { + viewports_.remove_if([viewport](const std::shared_ptr& v) { return v.get() == viewport;}); + + printf("There are now %d viewports in C++\n", viewports_.size()); + } + + + void EMSCRIPTEN_KEEPALIVE CreateWasmApplication(ViewportHandle viewport) { printf("CreateWasmApplication\n"); - viewport_.reset(new OrthancStone::WidgetViewport); - viewport_->SetStatusBar(statusBar_); - viewport_->Register(changeObserver_); - application.reset(CreateUserApplication()); @@ -122,7 +146,8 @@ } } - int EMSCRIPTEN_KEEPALIVE ViewportRender(unsigned int width, + int EMSCRIPTEN_KEEPALIVE ViewportRender(OrthancStone::WidgetViewport* viewport, + unsigned int width, unsigned int height, uint8_t* data) { @@ -138,10 +163,7 @@ Orthanc::ImageAccessor surface; surface.AssignWritable(Orthanc::PixelFormat_BGRA32, width, height, 4 * width, data); - if (viewport_.get() != NULL) - { - viewport_->Render(surface); - } + viewport->Render(surface); // Convert from BGRA32 memory layout (only color mode supported by // Cairo, which corresponds to CAIRO_FORMAT_ARGB32) to RGBA32 (as diff -r 1fa4c65c7e1b -r c8f11437a6fd Platforms/WebAssembly/Defaults.h --- a/Platforms/WebAssembly/Defaults.h Thu Jun 14 12:32:08 2018 +0200 +++ b/Platforms/WebAssembly/Defaults.h Thu Jun 14 13:28:40 2018 +0200 @@ -9,6 +9,8 @@ #include #include +typedef OrthancStone::WidgetViewport* ViewportHandle; // the objects exchanged between JS and C++ + #ifdef __cplusplus extern "C" { #endif @@ -17,7 +19,7 @@ extern void ScheduleRedraw(); // C++ methods accessible from JS - extern void EMSCRIPTEN_KEEPALIVE CreateWasmApplication(); + extern void EMSCRIPTEN_KEEPALIVE CreateWasmApplication(ViewportHandle viewport); // extern void EMSCRIPTEN_KEEPALIVE SetStartupParameter(const char* keyc, const char* value); // extern void EMSCRIPTEN_KEEPALIVE StartWasmApplication(); diff -r 1fa4c65c7e1b -r c8f11437a6fd Platforms/WebAssembly/wasm-application.js --- a/Platforms/WebAssembly/wasm-application.js Thu Jun 14 12:32:08 2018 +0200 +++ b/Platforms/WebAssembly/wasm-application.js Thu Jun 14 13:28:40 2018 +0200 @@ -47,26 +47,23 @@ function InitializeWasmApplication(canvasId) { console.log("Initializing wasm-app"); - viewport = new Stone.WasmViewport(StoneFrameworkModule, 'canvas'); - viewport.Initialize(); - /******************** */ + console.log("Connecting C++ methods to JS methods"); SetStartupParameter = StoneFrameworkModule.cwrap('SetStartupParameter', null, [ 'string', 'string' ]); - CreateWasmApplication = StoneFrameworkModule.cwrap('CreateWasmApplication', null, [ ], [ ]); + CreateWasmApplication = StoneFrameworkModule.cwrap('CreateWasmApplication', null, [ 'any' ], [ ]); + CreateCppViewport = StoneFrameworkModule.cwrap('CreateCppViewport', 'any', [ ], [ ]); + ReleaseCppViewport = StoneFrameworkModule.cwrap('ReleaseCppViewport', null, [ 'any' ], [ ]); StartWasmApplication = StoneFrameworkModule.cwrap('StartWasmApplication', null, [ ], [ ]); - - /******************** */ - - // NotifyGlobalParameter = StoneFrameworkModule.cwrap('NotifyGlobalParameter', null, - // [ 'string', 'string' ]); - // ViewportUpdate = StoneFrameworkModule.cwrap('ViewportUpdate', null, - // [ 'string' ]); WasmWebService_NotifySuccess = StoneFrameworkModule.cwrap('WasmWebService_NotifySuccess', null, [ 'number', 'string', 'array', 'number', 'number' ]); WasmWebService_NotifyError = StoneFrameworkModule.cwrap('WasmWebService_NotifyError', null, [ 'number', 'string', 'number' ]); - //NotifyRestApiGet = Module.cwrap('NotifyRestApiGet', null, [ 'number', 'array', 'number' ]); NotifyUpdateContent = StoneFrameworkModule.cwrap('NotifyUpdateContent', null, [ ]); + + console.log("Creating main viewport"); + + viewport = new Stone.WasmViewport(StoneFrameworkModule, 'canvas'); + viewport.Initialize(CreateCppViewport()); // Prevent scrolling document.body.addEventListener('touchmove', function(event) { diff -r 1fa4c65c7e1b -r c8f11437a6fd Platforms/WebAssembly/wasm-viewport.ts --- a/Platforms/WebAssembly/wasm-viewport.ts Thu Jun 14 12:32:08 2018 +0200 +++ b/Platforms/WebAssembly/wasm-viewport.ts Thu Jun 14 13:28:40 2018 +0200 @@ -29,6 +29,8 @@ private ViewportMouseWheel : Function; private ViewportKeyPressed : Function; + private pimpl_ : any; // Private pointer to the underlying WebAssembly C++ object + public constructor(module: any, canvasId: string) { this.module_ = module; this.canvasId_ = canvasId; @@ -36,7 +38,7 @@ this.context_ = this.htmlCanvas_.getContext('2d'); this.ViewportSetSize = this.module_.cwrap('ViewportSetSize', null, [ 'number', 'number' ]); - this.ViewportRender = this.module_.cwrap('ViewportRender', null, [ 'number', 'number', 'number', 'number' ]); + this.ViewportRender = this.module_.cwrap('ViewportRender', null, [ 'any', 'number', 'number', 'number' ]); this.ViewportMouseDown = this.module_.cwrap('ViewportMouseDown', null, [ 'number', 'number', 'number', 'number' ]); this.ViewportMouseMove = this.module_.cwrap('ViewportMouseMove', null, [ 'number', 'number' ]); this.ViewportMouseUp = this.module_.cwrap('ViewportMouseUp', null, [ ]); @@ -49,7 +51,8 @@ public Redraw() { if (this.imageData_ === null || this.renderingBuffer_ === null || - this.ViewportRender(this.imageData_.width, + this.ViewportRender(this.pimpl_, + this.imageData_.width, this.imageData_.height, this.renderingBuffer_) == 0) { console.log('The rendering has failed'); @@ -90,127 +93,129 @@ this.Redraw(); } - public Initialize() { - - // Force the rendering of the viewport for the first time - this.Resize(); - - // Register an event listener to call the Resize() function - // each time the window is resized. - window.addEventListener('resize', this.Resize, false); - - var that = this; - - this.htmlCanvas_.addEventListener('contextmenu', function(event) { - // Prevent right click on the canvas - event.preventDefault(); - }, false); - - this.htmlCanvas_.addEventListener('mouseleave', function(event) { - that.ViewportMouseLeave(); - }); + public Initialize(cppViewport: any) { + this.pimpl_ = cppViewport; + + console.log(this.pimpl_); + // Force the rendering of the viewport for the first time + this.Resize(); - this.htmlCanvas_.addEventListener('mouseenter', function(event) { - that.ViewportMouseEnter(); - }); - - this.htmlCanvas_.addEventListener('mousedown', function(event) { - var x = event.pageX - this.offsetLeft; - var y = event.pageY - this.offsetTop; - that.ViewportMouseDown(event.button, x, y, 0 /* TODO */); - }); - - this.htmlCanvas_.addEventListener('mousemove', function(event) { - var x = event.pageX - this.offsetLeft; - var y = event.pageY - this.offsetTop; - that.ViewportMouseMove(x, y); - }); - - this.htmlCanvas_.addEventListener('mouseup', function(event) { - that.ViewportMouseUp(); - }); - - window.addEventListener('keydown', function(event) { - that.ViewportKeyPressed(event.key, event.shiftKey, event.ctrlKey, event.altKey); - }); + // Register an event listener to call the Resize() function + // each time the window is resized. + window.addEventListener('resize', this.Resize, false); - this.htmlCanvas_.addEventListener('wheel', function(event) { - var x = event.pageX - this.offsetLeft; - var y = event.pageY - this.offsetTop; - that.ViewportMouseWheel(event.deltaY, x, y, event.ctrlKey); - event.preventDefault(); - }); - - var that = this; - this.htmlCanvas_.addEventListener('touchstart', function(event) { - that.ResetTouch(); - }); - - this.htmlCanvas_.addEventListener('touchend', function(event) { - that.ResetTouch(); - }); + var that = this; + + this.htmlCanvas_.addEventListener('contextmenu', function(event) { + // Prevent right click on the canvas + event.preventDefault(); + }, false); + + this.htmlCanvas_.addEventListener('mouseleave', function(event) { + that.ViewportMouseLeave(); + }); + + this.htmlCanvas_.addEventListener('mouseenter', function(event) { + that.ViewportMouseEnter(); + }); + + this.htmlCanvas_.addEventListener('mousedown', function(event) { + var x = event.pageX - this.offsetLeft; + var y = event.pageY - this.offsetTop; + that.ViewportMouseDown(event.button, x, y, 0 /* TODO */); + }); + + this.htmlCanvas_.addEventListener('mousemove', function(event) { + var x = event.pageX - this.offsetLeft; + var y = event.pageY - this.offsetTop; + that.ViewportMouseMove(x, y); + }); + + this.htmlCanvas_.addEventListener('mouseup', function(event) { + that.ViewportMouseUp(); + }); + + window.addEventListener('keydown', function(event) { + that.ViewportKeyPressed(event.key, event.shiftKey, event.ctrlKey, event.altKey); + }); - this.htmlCanvas_.addEventListener('touchmove', function(event) { - if (that.touchTranslation_.length == 2) { - var t = that.GetTouchTranslation(event); - that.ViewportMouseMove(t[0], t[1]); - } - else if (that.touchZoom_.length == 3) { - var z0 = that.touchZoom_; - var z1 = that.GetTouchZoom(event); - that.ViewportMouseMove(z0[0], z0[1] - z0[2] + z1[2]); - } - else { - // Realize the gesture event - if (event.targetTouches.length == 1) { - // Exactly one finger inside the canvas => Setup a translation - that.touchTranslation_ = that.GetTouchTranslation(event); - that.ViewportMouseDown(1 /* middle button */, - that.touchTranslation_[0], - that.touchTranslation_[1], 0); - } else if (event.targetTouches.length == 2) { - // Exactly 2 fingers inside the canvas => Setup a pinch/zoom - that.touchZoom_ = that.GetTouchZoom(event); + this.htmlCanvas_.addEventListener('wheel', function(event) { + var x = event.pageX - this.offsetLeft; + var y = event.pageY - this.offsetTop; + that.ViewportMouseWheel(event.deltaY, x, y, event.ctrlKey); + event.preventDefault(); + }); + + var that = this; + this.htmlCanvas_.addEventListener('touchstart', function(event) { + that.ResetTouch(); + }); + + this.htmlCanvas_.addEventListener('touchend', function(event) { + that.ResetTouch(); + }); + + this.htmlCanvas_.addEventListener('touchmove', function(event) { + if (that.touchTranslation_.length == 2) { + var t = that.GetTouchTranslation(event); + that.ViewportMouseMove(t[0], t[1]); + } + else if (that.touchZoom_.length == 3) { var z0 = that.touchZoom_; - that.ViewportMouseDown(2 /* right button */, - z0[0], - z0[1], 0); - } - } - }); - } + var z1 = that.GetTouchZoom(event); + that.ViewportMouseMove(z0[0], z0[1] - z0[2] + z1[2]); + } + else { + // Realize the gesture event + if (event.targetTouches.length == 1) { + // Exactly one finger inside the canvas => Setup a translation + that.touchTranslation_ = that.GetTouchTranslation(event); + that.ViewportMouseDown(1 /* middle button */, + that.touchTranslation_[0], + that.touchTranslation_[1], 0); + } else if (event.targetTouches.length == 2) { + // Exactly 2 fingers inside the canvas => Setup a pinch/zoom + that.touchZoom_ = that.GetTouchZoom(event); + var z0 = that.touchZoom_; + that.ViewportMouseDown(2 /* right button */, + z0[0], + z0[1], 0); + } + } + }); + } public ResetTouch() { - if (this.touchTranslation_ || - this.touchZoom_) { - this.ViewportMouseUp(); - } - - this.touchTranslation_ = false; - this.touchZoom_ = false; + if (this.touchTranslation_ || + this.touchZoom_) { + this.ViewportMouseUp(); } + + this.touchTranslation_ = false; + this.touchZoom_ = false; + } public GetTouchTranslation(event) { - var touch = event.targetTouches[0]; - return [ - touch.pageX, - touch.pageY - ]; - } + var touch = event.targetTouches[0]; + return [ + touch.pageX, + touch.pageY + ]; + } public GetTouchZoom(event) { - var touch1 = event.targetTouches[0]; - var touch2 = event.targetTouches[1]; - var dx = (touch1.pageX - touch2.pageX); - var dy = (touch1.pageY - touch2.pageY); - var d = Math.sqrt(dx * dx + dy * dy); - return [ - (touch1.pageX + touch2.pageX) / 2.0, - (touch1.pageY + touch2.pageY) / 2.0, - d - ]; - } + var touch1 = event.targetTouches[0]; + var touch2 = event.targetTouches[1]; + var dx = (touch1.pageX - touch2.pageX); + var dy = (touch1.pageY - touch2.pageY); + var d = Math.sqrt(dx * dx + dy * dy); + return [ + (touch1.pageX + touch2.pageX) / 2.0, + (touch1.pageY + touch2.pageY) / 2.0, + d + ]; + } - } +} } \ No newline at end of file