changeset 616:97926984d5d0

WebAssembly sample using Scene2D
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 02 May 2019 13:27:41 +0200
parents b4de8272e8fb
children 7efa2543699d
files Framework/OpenGL/OpenGLTexture.cpp Framework/OpenGL/WebAssemblyOpenGLContext.cpp Framework/OpenGL/WebAssemblyOpenGLContext.h Framework/Scene2D/Internals/OpenGLShaderVersionDirective.h Resources/CMake/OrthancStoneConfiguration.cmake Resources/CMake/OrthancStoneParameters.cmake Samples/Sdl/CMakeLists.txt Samples/WebAssembly/BasicScene.cpp Samples/WebAssembly/BasicScene.html Samples/WebAssembly/CMakeLists.txt Samples/WebAssembly/Configuration.json Samples/WebAssembly/index.html
diffstat 12 files changed, 713 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/Framework/OpenGL/OpenGLTexture.cpp	Thu May 02 10:35:42 2019 +0200
+++ b/Framework/OpenGL/OpenGLTexture.cpp	Thu May 02 13:27:41 2019 +0200
@@ -74,7 +74,7 @@
 
         case Orthanc::PixelFormat_RGB24:
           sourceFormat = GL_RGB;
-          internalFormat = GL_RGBA;
+          internalFormat = GL_RGB;
           break;
 
         case Orthanc::PixelFormat_RGBA32:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/OpenGL/WebAssemblyOpenGLContext.cpp	Thu May 02 13:27:41 2019 +0200
@@ -0,0 +1,168 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "WebAssemblyOpenGLContext.h"
+
+#if ORTHANC_ENABLE_WASM == 1
+
+#include <Core/OrthancException.h>
+
+#include <emscripten/html5.h>
+#include <boost/math/special_functions/round.hpp>
+
+namespace OrthancStone
+{
+  namespace OpenGL
+  {
+    class WebAssemblyOpenGLContext::PImpl
+    {
+    private:
+      std::string                      canvas_;
+      EMSCRIPTEN_WEBGL_CONTEXT_HANDLE  context_;
+      unsigned int                     canvasWidth_;
+      unsigned int                     canvasHeight_;
+
+    public:
+      PImpl(const std::string& canvas) :
+        canvas_(canvas)
+      {
+        // Context configuration
+        EmscriptenWebGLContextAttributes attr; 
+        emscripten_webgl_init_context_attributes(&attr);
+
+        context_ = emscripten_webgl_create_context(canvas.c_str(), &attr);
+        if (context_ < 0)
+        {
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
+                                          "Cannot create an OpenGL context for canvas: " + canvas);
+        }
+
+        UpdateSize();
+      }
+
+      ~PImpl()
+      {
+        emscripten_webgl_destroy_context(context_);
+      }
+
+      void MakeCurrent()
+      {
+        if (emscripten_webgl_make_context_current(context_) != EMSCRIPTEN_RESULT_SUCCESS)
+        {
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
+                                          "Cannot set the OpenGL context");
+        }
+      }
+
+      void SwapBuffer() 
+      {
+        /**
+         * "Rendered WebGL content is implicitly presented (displayed to
+         * the user) on the canvas when the event handler that renders with
+         * WebGL returns back to the browser event loop."
+         * https://emscripten.org/docs/api_reference/html5.h.html#webgl-context
+         *
+         * Could call "emscripten_webgl_commit_frame()" if
+         * "explicitSwapControl" option were set to "true".
+         **/
+      }
+
+      unsigned int GetCanvasWidth() const
+      {
+        return canvasWidth_;
+      }
+
+      unsigned int GetCanvasHeight() const
+      {
+        return canvasHeight_;
+      }
+
+      void UpdateSize()
+      {
+        double w, h;
+        emscripten_get_element_css_size(canvas_.c_str(), &w, &h);
+
+        /**
+         * Emscripten has the function emscripten_get_element_css_size()
+         * to query the width and height of a named HTML element. I'm
+         * calling this first to get the initial size of the canvas DOM
+         * element, and then call emscripten_set_canvas_size() to
+         * initialize the framebuffer size of the canvas to the same
+         * size as its DOM element.
+         * https://floooh.github.io/2017/02/22/emsc-html.html
+         **/
+
+        if (w <= 0 ||
+            h <= 0)
+        {
+          canvasWidth_ = 0;
+          canvasHeight_ = 0;
+        }
+        else
+        {
+          canvasWidth_ = static_cast<unsigned int>(boost::math::iround(w));
+          canvasHeight_ = static_cast<unsigned int>(boost::math::iround(h));
+        }
+    
+        emscripten_set_canvas_element_size(canvas_.c_str(), canvasWidth_, canvasHeight_);
+      }
+    };
+
+
+    WebAssemblyOpenGLContext::WebAssemblyOpenGLContext(const std::string& canvas) :
+      pimpl_(new PImpl(canvas))
+    {
+    }
+
+    void WebAssemblyOpenGLContext::MakeCurrent()
+    {
+      assert(pimpl_.get() != NULL);
+      pimpl_->MakeCurrent();
+    }
+
+
+    void WebAssemblyOpenGLContext::SwapBuffer() 
+    {
+      assert(pimpl_.get() != NULL);
+      pimpl_->SwapBuffer();
+    }
+
+    unsigned int WebAssemblyOpenGLContext::GetCanvasWidth() const
+    {
+      assert(pimpl_.get() != NULL);
+      return pimpl_->GetCanvasWidth();
+    }
+
+    unsigned int WebAssemblyOpenGLContext::GetCanvasHeight() const
+    {
+      assert(pimpl_.get() != NULL);
+      return pimpl_->GetCanvasHeight();
+    }
+
+    void WebAssemblyOpenGLContext::UpdateSize()
+    {
+      assert(pimpl_.get() != NULL);
+      pimpl_->UpdateSize();
+    }
+  }
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/OpenGL/WebAssemblyOpenGLContext.h	Thu May 02 13:27:41 2019 +0200
@@ -0,0 +1,60 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#if !defined(ORTHANC_ENABLE_WASM)
+#  error Macro ORTHANC_ENABLE_WASM must be defined
+#endif
+
+#if ORTHANC_ENABLE_WASM == 1
+
+#include "IOpenGLContext.h"
+
+#include <boost/shared_ptr.hpp>
+
+namespace OrthancStone
+{
+  namespace OpenGL
+  {
+    class WebAssemblyOpenGLContext : public OpenGL::IOpenGLContext
+    {
+    private:
+      class PImpl;
+      boost::shared_ptr<PImpl>  pimpl_;
+
+    public:
+      WebAssemblyOpenGLContext(const std::string& canvas);
+
+      virtual void MakeCurrent();
+
+      virtual void SwapBuffer();
+
+      virtual unsigned int GetCanvasWidth() const;
+
+      virtual unsigned int GetCanvasHeight() const;
+
+      void UpdateSize();
+    };
+  }
+}
+
+#endif
--- a/Framework/Scene2D/Internals/OpenGLShaderVersionDirective.h	Thu May 02 10:35:42 2019 +0200
+++ b/Framework/Scene2D/Internals/OpenGLShaderVersionDirective.h	Thu May 02 13:27:41 2019 +0200
@@ -1,3 +1,8 @@
 #pragma once
 
-#define ORTHANC_STONE_OPENGL_SHADER_VERSION_DIRECTIVE "#version 110      \n"
\ No newline at end of file
+#if ORTHANC_ENABLE_WASM == 1
+// https://emscripten.org/docs/optimizing/Optimizing-WebGL.html
+#  define ORTHANC_STONE_OPENGL_SHADER_VERSION_DIRECTIVE "precision mediump float;\n"
+#else
+#  define ORTHANC_STONE_OPENGL_SHADER_VERSION_DIRECTIVE "#version 110\n"
+#endif
--- a/Resources/CMake/OrthancStoneConfiguration.cmake	Thu May 02 10:35:42 2019 +0200
+++ b/Resources/CMake/OrthancStoneConfiguration.cmake	Thu May 02 13:27:41 2019 +0200
@@ -56,6 +56,27 @@
     message(FATAL_ERROR "Cannot enable SSL in sandboxed environments")
   endif()
 endif()
+
+if (ENABLE_WASM)
+  if (NOT ORTHANC_SANDBOXED)
+    message(FATAL_ERROR "WebAssembly target must me configured as sandboxed")
+  endif()
+
+  if (NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
+    message(FATAL_ERROR "WebAssembly target requires the emscripten compiler")    
+  endif()
+
+  add_definitions(-DORTHANC_ENABLE_WASM=1)
+else()
+  if (CMAKE_SYSTEM_NAME STREQUAL "Emscripten" OR
+      CMAKE_SYSTEM_NAME STREQUAL "PNaCl" OR
+      CMAKE_SYSTEM_NAME STREQUAL "NaCl32" OR
+      CMAKE_SYSTEM_NAME STREQUAL "NaCl64")
+    message(FATAL_ERROR "Trying to use a Web compiler for a native build")
+  endif()
+
+  add_definitions(-DORTHANC_ENABLE_WASM=0)
+endif()
   
 
 #####################################################################
@@ -441,6 +462,12 @@
     ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLTextRenderer.cpp
     ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLTextureProgram.cpp
     )
+
+  if (ENABLE_WASM)
+    list(APPEND ORTHANC_STONE_SOURCES
+      ${ORTHANC_STONE_ROOT}/Framework/OpenGL/WebAssemblyOpenGLContext.cpp
+      )
+  endif()
 endif()
 
 
--- a/Resources/CMake/OrthancStoneParameters.cmake	Thu May 02 10:35:42 2019 +0200
+++ b/Resources/CMake/OrthancStoneParameters.cmake	Thu May 02 13:27:41 2019 +0200
@@ -53,3 +53,4 @@
 #####################################################################
 
 set(ENABLE_OPENGL ON CACHE INTERNAL "Enable support of OpenGL")
+set(ENABLE_WASM OFF CACHE INTERNAL "Enable support of WebAssembly")
--- a/Samples/Sdl/CMakeLists.txt	Thu May 02 10:35:42 2019 +0200
+++ b/Samples/Sdl/CMakeLists.txt	Thu May 02 13:27:41 2019 +0200
@@ -46,8 +46,6 @@
 
 include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneConfiguration.cmake)
 
-include_directories(${ORTHANC_STONE_ROOT})
-
 
 #####################################################################
 ## Build the samples
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/WebAssembly/BasicScene.cpp	Thu May 02 13:27:41 2019 +0200
@@ -0,0 +1,253 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+
+#include <emscripten.h>
+#include <emscripten/html5.h>
+
+// From Stone
+#include "../../Applications/Sdl/SdlOpenGLWindow.h"
+#include "../../Framework/Scene2D/CairoCompositor.h"
+#include "../../Framework/Scene2D/ColorTextureSceneLayer.h"
+#include "../../Framework/Scene2D/OpenGLCompositor.h"
+#include "../../Framework/Scene2D/PanSceneTracker.h"
+#include "../../Framework/Scene2D/RotateSceneTracker.h"
+#include "../../Framework/Scene2D/Scene2D.h"
+#include "../../Framework/Scene2D/ZoomSceneTracker.h"
+#include "../../Framework/StoneInitialization.h"
+#include "../../Framework/OpenGL/WebAssemblyOpenGLContext.h"
+
+// From Orthanc framework
+#include <Core/Images/Image.h>
+
+#include <stdio.h>
+
+static const unsigned int FONT_SIZE = 32;
+
+
+void PrepareScene(OrthancStone::Scene2D& scene)
+{
+  using namespace OrthancStone;
+
+  // Texture of 2x2 size
+  if (1)
+  {
+    Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false);
+    
+    uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0));
+    p[0] = 255;
+    p[1] = 0;
+    p[2] = 0;
+
+    p[3] = 0;
+    p[4] = 255;
+    p[5] = 0;
+
+    p = reinterpret_cast<uint8_t*>(i.GetRow(1));
+    p[0] = 0;
+    p[1] = 0;
+    p[2] = 255;
+
+    p[3] = 255;
+    p[4] = 0;
+    p[5] = 0;
+
+    scene.SetLayer(12, new ColorTextureSceneLayer(i));
+
+    std::auto_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i));
+    l->SetOrigin(-3, 2);
+    l->SetPixelSpacing(1.5, 1);
+    l->SetAngle(20.0 / 180.0 * M_PI);
+    scene.SetLayer(14, l.release());
+  }
+
+  // Texture of 1x1 size
+  if (1)
+  {
+    Orthanc::Image i(Orthanc::PixelFormat_RGB24, 1, 1, false);
+    
+    uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0));
+    p[0] = 255;
+    p[1] = 0;
+    p[2] = 0;
+
+    std::auto_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i));
+    l->SetOrigin(-2, 1);
+    l->SetAngle(20.0 / 180.0 * M_PI);
+    scene.SetLayer(13, l.release());
+  }
+
+  // Some lines
+  {
+    std::auto_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer);
+
+    layer->SetThickness(1);
+
+    PolylineSceneLayer::Chain chain;
+    chain.push_back(ScenePoint2D(0 - 0.5, 0 - 0.5));
+    chain.push_back(ScenePoint2D(0 - 0.5, 2 - 0.5));
+    chain.push_back(ScenePoint2D(2 - 0.5, 2 - 0.5));
+    chain.push_back(ScenePoint2D(2 - 0.5, 0 - 0.5));
+    layer->AddChain(chain, true);
+
+    chain.clear();
+    chain.push_back(ScenePoint2D(-5, -5));
+    chain.push_back(ScenePoint2D(5, -5));
+    chain.push_back(ScenePoint2D(5, 5));
+    chain.push_back(ScenePoint2D(-5, 5));
+    layer->AddChain(chain, true);
+
+    double dy = 1.01;
+    chain.clear();
+    chain.push_back(ScenePoint2D(-4, -4));
+    chain.push_back(ScenePoint2D(4, -4 + dy));
+    chain.push_back(ScenePoint2D(-4, -4 + 2.0 * dy));
+    chain.push_back(ScenePoint2D(4, 2));
+    layer->AddChain(chain, false);
+
+    layer->SetColor(0,255, 255);
+    scene.SetLayer(50, layer.release());
+  }
+
+  // Some text
+  if (1)
+  {
+    std::auto_ptr<TextSceneLayer> layer(new TextSceneLayer);
+    layer->SetText("Hello");
+    scene.SetLayer(100, layer.release());
+  }
+}
+
+
+
+
+
+class WebAssemblyCanvas : public boost::noncopyable
+{
+private:
+  OrthancStone::OpenGL::WebAssemblyOpenGLContext  context_;
+  OrthancStone::Scene2D                           scene_;
+  OrthancStone::OpenGLCompositor                  compositor_;
+
+public:
+  WebAssemblyCanvas(const std::string& canvas) :
+    context_(canvas),
+    compositor_(context_, scene_)
+  {
+    compositor_.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, 
+                        FONT_SIZE, Orthanc::Encoding_Latin1);
+  }
+
+  OrthancStone::Scene2D& GetScene()
+  {
+    return scene_;
+  }
+
+  void UpdateSize()
+  {
+    context_.UpdateSize();
+    compositor_.UpdateSize();
+    Refresh();
+  }
+
+  void Refresh()
+  {
+    compositor_.Refresh();
+  }
+};
+
+
+
+std::auto_ptr<WebAssemblyCanvas>  canvas1_;
+std::auto_ptr<WebAssemblyCanvas>  canvas2_;
+std::auto_ptr<WebAssemblyCanvas>  canvas3_;
+
+
+EM_BOOL OnWindowResize(int eventType, const EmscriptenUiEvent *uiEvent, void *userData)
+{
+  if (canvas1_.get() != NULL)
+  {
+    canvas1_->UpdateSize();
+  }
+  
+  if (canvas2_.get() != NULL)
+  {
+    canvas2_->UpdateSize();
+  }
+  
+  if (canvas3_.get() != NULL)
+  {
+    canvas3_->UpdateSize();
+  }
+  
+  return true;
+}
+
+
+EM_BOOL OnMouseClick(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData)
+{
+  if (userData != NULL)
+  {
+    WebAssemblyCanvas& canvas = *reinterpret_cast<WebAssemblyCanvas*>(userData);
+
+    static unsigned int count = 0;
+    char buf[64];
+    sprintf(buf, "click %d", count++);
+
+    std::auto_ptr<OrthancStone::TextSceneLayer> layer(new OrthancStone::TextSceneLayer);
+    layer->SetText(buf);
+    canvas.GetScene().SetLayer(100, layer.release());
+    canvas.Refresh();
+  }
+
+  return true;
+}
+
+
+extern "C"
+{
+  int main(int argc, char const *argv[]) 
+  {
+    OrthancStone::StoneInitialize();
+    EM_ASM(window.dispatchEvent(new CustomEvent("WebAssemblyLoaded")););
+  }
+
+  EMSCRIPTEN_KEEPALIVE
+  void Initialize()
+  {
+    canvas1_.reset(new WebAssemblyCanvas("mycanvas1"));
+    PrepareScene(canvas1_->GetScene());
+    canvas1_->UpdateSize();
+
+    canvas2_.reset(new WebAssemblyCanvas("mycanvas2"));
+    PrepareScene(canvas2_->GetScene());
+    canvas2_->UpdateSize();
+
+    canvas3_.reset(new WebAssemblyCanvas("mycanvas3"));
+    PrepareScene(canvas3_->GetScene());
+    canvas3_->UpdateSize();
+
+    emscripten_set_resize_callback("#window", NULL, false, OnWindowResize);
+    emscripten_set_click_callback("mycanvas1", canvas1_.get(), false, OnMouseClick);
+    emscripten_set_click_callback("mycanvas2", canvas2_.get(), false, OnMouseClick);
+    emscripten_set_click_callback("mycanvas3", canvas3_.get(), false, OnMouseClick);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/WebAssembly/BasicScene.html	Thu May 02 13:27:41 2019 +0200
@@ -0,0 +1,69 @@
+<!doctype html>
+<html lang="en-us">
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+
+    <!-- Disable pinch zoom on mobile devices -->
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+    <meta name="HandheldFriendly" content="true" />
+    
+    
+    <title>Stone of Orthanc</title>
+
+    <style>
+      html, body {
+      width: 100%;
+      height: 100%;
+      margin: 0px;
+      border: 0;
+      overflow: hidden; /*  Disable scrollbars */
+      display: block;  /* No floating content on sides */
+      }
+
+      #mycanvas1 {
+      position:absolute;
+      left:0%;
+      top:0%;
+      background-color: red;
+      width: 50%;
+      height: 100%;
+      }
+
+      #mycanvas2 {
+      position:absolute;
+      left:50%;
+      top:0%;
+      background-color: green;
+      width: 50%;
+      height: 50%;
+      }
+
+      #mycanvas3 {
+      position:absolute;
+      left:50%;
+      top:50%;
+      background-color: blue;
+      width: 50%;
+      height: 50%;
+      }
+    </style>
+  </head>
+  <body>
+    <canvas id="mycanvas1"></canvas>
+    <canvas id="mycanvas2"></canvas>
+    <canvas id="mycanvas3"></canvas>
+
+    <script type="text/javascript">
+      if (!('WebAssembly' in window)) {
+      alert('Sorry, your browser does not support WebAssembly :(');
+      } else {
+      window.addEventListener('WebAssemblyLoaded', function() {
+      Module.ccall('Initialize', null, null, null);
+      });
+      }
+    </script>
+
+    <script type="text/javascript" async src="BasicScene.js"></script>
+  </body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/WebAssembly/CMakeLists.txt	Thu May 02 13:27:41 2019 +0200
@@ -0,0 +1,96 @@
+
+# source ~/Downloads/emsdk/emsdk_env.sh
+# cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON .. -DCMAKE_INSTALL_PREFIX=/tmp/stone
+# ninja install
+# sudo docker run -p 4242:4242 -p 8042:8042 --rm -v /tmp/stone:/root/stone:ro jodogne/orthanc-plugins:1.5.6 /root/stone/Configuration.json --verbose
+
+
+cmake_minimum_required(VERSION 2.8.3)
+
+
+#####################################################################
+## Configuration of the Emscripten compiler for WebAssembly target
+#####################################################################
+
+set(WASM_FLAGS "-s WASM=1 -s FETCH=1")
+
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WASM_FLAGS}")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WASM_FLAGS}")
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]'")
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ERROR_ON_UNDEFINED_SYMBOLS=1")
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1")
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ASSERTIONS=1 -s DISABLE_EXCEPTION_CATCHING=0")
+
+
+#####################################################################
+## Configuration of the Orthanc framework
+#####################################################################
+
+# This CMake file defines the "ORTHANC_STONE_VERSION" macro, so it
+# must be the first inclusion
+include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/Version.cmake)
+
+if (ORTHANC_STONE_VERSION STREQUAL "mainline")
+  set(ORTHANC_FRAMEWORK_VERSION "mainline")
+  set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg")
+else()
+  set(ORTHANC_FRAMEWORK_VERSION "1.5.7")
+  set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web")
+endif()
+
+set(ORTHANC_FRAMEWORK_SOURCE "${ORTHANC_FRAMEWORK_DEFAULT_SOURCE}" CACHE STRING "Source of the Orthanc source code (can be \"hg\", \"archive\", \"web\" or \"path\")")
+set(ORTHANC_FRAMEWORK_ARCHIVE "" CACHE STRING "Path to the Orthanc archive, if ORTHANC_FRAMEWORK_SOURCE is \"archive\"")
+set(ORTHANC_FRAMEWORK_ROOT "" CACHE STRING "Path to the Orthanc source directory, if ORTHANC_FRAMEWORK_SOURCE is \"path\"")
+
+
+#####################################################################
+## Configuration of the Stone framework
+#####################################################################
+
+include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneParameters.cmake)
+include(${ORTHANC_ROOT}/Resources/CMake/DownloadPackage.cmake)
+
+DownloadPackage(
+  "a24b8136b8f3bb93f166baf97d9328de"
+  "http://orthanc.osimis.io/ThirdPartyDownloads/ubuntu-font-family-0.83.zip"
+  "${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83")
+
+set(ORTHANC_STONE_APPLICATION_RESOURCES
+  UBUNTU_FONT  ${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83/Ubuntu-R.ttf
+  )
+
+SET(ENABLE_GOOGLE_TEST OFF)
+SET(ENABLE_LOCALE ON)
+SET(ORTHANC_SANDBOXED ON)
+SET(ENABLE_WASM ON)
+
+include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneConfiguration.cmake)
+
+
+#####################################################################
+## Build the samples
+#####################################################################
+
+add_library(OrthancStone STATIC
+  ${ORTHANC_STONE_SOURCES}
+  )
+
+add_executable(BasicScene
+  BasicScene.cpp
+  )
+
+target_link_libraries(BasicScene OrthancStone)
+
+install(
+  TARGETS BasicScene
+  RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}
+  )
+
+install(
+  FILES
+  ${CMAKE_CURRENT_BINARY_DIR}/BasicScene.wasm
+  ${CMAKE_SOURCE_DIR}/BasicScene.html
+  ${CMAKE_SOURCE_DIR}/Configuration.json
+  ${CMAKE_SOURCE_DIR}/index.html
+  DESTINATION ${CMAKE_INSTALL_PREFIX}
+  )
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/WebAssembly/Configuration.json	Thu May 02 13:27:41 2019 +0200
@@ -0,0 +1,17 @@
+{
+  "Plugins": [
+    "/usr/local/share/orthanc/plugins/libOrthancWebViewer.so",
+    "/usr/local/share/orthanc/plugins/libServeFolders.so"
+  ],
+  "StorageDirectory" : "/var/lib/orthanc/db",
+  "IndexDirectory" : "/var/lib/orthanc/db",
+  "RemoteAccessAllowed" : true,
+  "AuthenticationEnabled" : false,
+  "ServeFolders" : {
+    "AllowCache" : false,
+    "GenerateETag" : true,
+    "Folders" : {
+      "/stone" : "/root/stone"
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/WebAssembly/index.html	Thu May 02 13:27:41 2019 +0200
@@ -0,0 +1,15 @@
+<!doctype html>
+<html lang="en-us">
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+
+    <title>Stone of Orthanc</title>
+  </head>
+  <body>
+    <h1>Available samples</h1>
+    <ul>
+      <li><a href="BasicScene.html">Basic scene</a></li>
+    </ul>
+  </body>
+</html>