changeset 1348:eac254fb6791 broker

Moved Application/Samples/* to Application/Samples/Deprecated/* (remaining) + moved Samples/* to Samples/Deprecated/*
author Benjamin Golinvaux <bgo@osimis.io>
date Tue, 07 Apr 2020 14:31:28 +0200
parents bfd77672d825
children b1e6bef86955
files Applications/Samples/CMakeLists.txt Applications/Samples/Deprecated/CMakeLists.txt Samples/Deprecated/MultiPlatform/BasicScene/BasicScene.cpp Samples/Deprecated/MultiPlatform/BasicScene/BasicScene.h Samples/Deprecated/MultiPlatform/BasicScene/mainQt.cpp Samples/Deprecated/MultiPlatform/BasicScene/mainSdl.cpp Samples/Deprecated/Qt/BasicSceneWindow.cpp Samples/Deprecated/Qt/BasicSceneWindow.h Samples/Deprecated/Qt/BasicSceneWindow.ui Samples/Deprecated/Qt/CMakeLists.txt Samples/Deprecated/Qt/QStoneOpenGlWidget.cpp Samples/Deprecated/Qt/QStoneOpenGlWidget.h Samples/Deprecated/Qt/Scene2DInteractor.cpp Samples/Deprecated/Qt/Scene2DInteractor.h Samples/Deprecated/Sdl/BasicScene.cpp Samples/Deprecated/Sdl/CMakeLists.txt Samples/Deprecated/Sdl/FusionMprSdl.cpp Samples/Deprecated/Sdl/FusionMprSdl.h Samples/Deprecated/Sdl/Loader.cpp Samples/Deprecated/Sdl/RadiographyEditor.cpp Samples/Deprecated/Sdl/TrackerSample.cpp Samples/Deprecated/Sdl/TrackerSampleApp.cpp Samples/Deprecated/Sdl/TrackerSampleApp.h Samples/Deprecated/Sdl/cpp.hint Samples/Deprecated/WebAssembly/BasicMPR.cpp Samples/Deprecated/WebAssembly/BasicMPR.html Samples/Deprecated/WebAssembly/BasicScene.cpp Samples/Deprecated/WebAssembly/BasicScene.html Samples/Deprecated/WebAssembly/CMakeLists.txt Samples/Deprecated/WebAssembly/Configuration.json Samples/Deprecated/WebAssembly/ConfigurationLocalSJO.json Samples/Deprecated/WebAssembly/NOTES.txt Samples/Deprecated/WebAssembly/app.js Samples/Deprecated/WebAssembly/dev.h Samples/Deprecated/WebAssembly/index.html Samples/MultiPlatform/BasicScene/BasicScene.cpp Samples/MultiPlatform/BasicScene/BasicScene.h Samples/MultiPlatform/BasicScene/mainQt.cpp Samples/MultiPlatform/BasicScene/mainSdl.cpp Samples/Qt/BasicSceneWindow.cpp Samples/Qt/BasicSceneWindow.h Samples/Qt/BasicSceneWindow.ui Samples/Qt/CMakeLists.txt Samples/Qt/QStoneOpenGlWidget.cpp Samples/Qt/QStoneOpenGlWidget.h Samples/Qt/Scene2DInteractor.cpp Samples/Qt/Scene2DInteractor.h Samples/Sdl/BasicScene.cpp Samples/Sdl/CMakeLists.txt Samples/Sdl/FusionMprSdl.cpp Samples/Sdl/FusionMprSdl.h Samples/Sdl/Loader.cpp Samples/Sdl/RadiographyEditor.cpp Samples/Sdl/TrackerSample.cpp Samples/Sdl/TrackerSampleApp.cpp Samples/Sdl/TrackerSampleApp.h Samples/Sdl/cpp.hint Samples/WebAssembly/BasicMPR.cpp Samples/WebAssembly/BasicMPR.html Samples/WebAssembly/BasicScene.cpp Samples/WebAssembly/BasicScene.html Samples/WebAssembly/CMakeLists.txt Samples/WebAssembly/Configuration.json Samples/WebAssembly/ConfigurationLocalSJO.json Samples/WebAssembly/NOTES.txt Samples/WebAssembly/app.js Samples/WebAssembly/dev.h Samples/WebAssembly/index.html
diffstat 68 files changed, 6117 insertions(+), 6117 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/Samples/CMakeLists.txt	Tue Apr 07 14:29:01 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,292 +0,0 @@
-# Usage (Linux):
-# to build the WASM samples
-# source ~/Downloads/emsdk/emsdk_env.sh && cmake -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DSTONE_SOURCES_DIR=$currentDir/../../../orthanc-stone -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT=$currentDir/../../../orthanc -DALLOW_DOWNLOADS=ON .. -DENABLE_WASM=ON
-# to build the Qt samples
-
-cmake_minimum_required(VERSION 2.8.3)
-project(OrthancStone)
-
-include(../../Resources/CMake/OrthancStoneParameters.cmake)
-
-set(ENABLE_STONE_DEPRECATED ON)  # Need deprecated classes for these samples
-set(EMSCRIPTEN_SET_LLVM_WASM_BACKEND ON)
-
-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
-  )
-
-if (OPENSSL_NO_CAPIENG)
-add_definitions(-DOPENSSL_NO_CAPIENG=1)
-endif()
-
-
-# the following block has been borrowed from orthanc/**/Compiler.cmake
-if (MSVC_MULTIPLE_PROCESSES)
-# "If you omit the processMax argument in the /MP option, the
-# compiler obtains the number of effective processors from the
-# operating system, and then creates one process per effective
-# processor"
-# https://blog.kitware.com/cmake-building-with-all-your-cores/
-# https://docs.microsoft.com/en-us/cpp/build/reference/mp-build-with-multiple-processes
-set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP")
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
-endif()
-
-#set(ENABLE_DCMTK ON)
-
-set(ENABLE_SDL OFF CACHE BOOL "Target SDL Native application")
-set(ENABLE_QT OFF CACHE BOOL "Target Qt Native application")
-set(ENABLE_WASM OFF CACHE BOOL "Target WASM application")
-
-if (ENABLE_WASM)
-  #####################################################################
-  ## Configuration of the Emscripten compiler for WebAssembly target
-  #####################################################################
-
-  set(WASM_FLAGS "-s WASM=1")
-  set(WASM_FLAGS "${WASM_FLAGS} -s STRICT=1") # drops support for all deprecated build options
-  set(WASM_FLAGS "${WASM_FLAGS} -s FILESYSTEM=1") # if we don't include it, gen_uuid.c fails to build because srand, getpid(), ... are not defined
-  set(WASM_FLAGS "${WASM_FLAGS} -s DISABLE_EXCEPTION_CATCHING=0") # actually enable exception catching 
-  set(WASM_FLAGS "${WASM_FLAGS} -s ERROR_ON_MISSING_LIBRARIES=1")
-  
-  if (CMAKE_BUILD_TYPE MATCHES DEBUG)
-    set(WASM_FLAGS "${WASM_FLAGS} -g4") # generate debug information
-    set(WASM_FLAGS "${WASM_FLAGS} -s ASSERTIONS=2") # more runtime checks
-  else()
-    set(WASM_FLAGS "${WASM_FLAGS} -Os") # optimize for web (speed and size)
-  endif()
-
-  set(WASM_MODULE_NAME "StoneFrameworkModule" CACHE STRING "Name of the WebAssembly module")
-
-  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} ${WASM_FLAGS}")  # not always clear which flags are for the compiler and which one are for the linker -> pass them all to the linker too
-  # set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Applications/Samples/samples-library.js")
-  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmWebService.js")
-  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmDelayedCallExecutor.js")
-  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/default-library.js")
-  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 EXPORT_NAME='\"${WASM_MODULE_NAME}\"'")
-  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1")
-  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s TOTAL_MEMORY=536870912")
-  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s TOTAL_STACK=128000000")
-
-  add_definitions(-DORTHANC_ENABLE_WASM=1)
-  set(ORTHANC_SANDBOXED ON)
-
-elseif (ENABLE_QT OR ENABLE_SDL)
-
-  set(ENABLE_NATIVE ON)
-  set(ORTHANC_SANDBOXED OFF)
-  set(ENABLE_CRYPTO_OPTIONS ON)
-  set(ENABLE_GOOGLE_TEST ON)
-  set(ENABLE_WEB_CLIENT ON)
-
-else()
-  set(ENABLE_NATIVE ON)
-  set(ENABLE_OPENGL OFF)
-  
-endif()
-
-
-#####################################################################
-## Configuration for Orthanc
-#####################################################################
-
-# include(../../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.4.1")
-  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\"")
-
-add_definitions(
-  -DORTHANC_ENABLE_LOGGING_PLUGIN=0
-  )
-
-
-#####################################################################
-## Build a static library containing the Orthanc Stone framework
-#####################################################################
-
-
-LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options)
-
-include(../../Resources/CMake/OrthancStoneConfiguration.cmake)
-
-add_library(OrthancStone STATIC
-  ${ORTHANC_STONE_SOURCES}
-  )
-
-#####################################################################
-## Build all the sample applications
-#####################################################################
-
-include_directories(${ORTHANC_STONE_ROOT})
-
-# files common to all samples
-list(APPEND SAMPLE_APPLICATIONS_SOURCES
-  ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleInteractor.h
-  ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleApplicationBase.h
-  )
-
-if (ENABLE_QT)
-  list(APPEND SAMPLE_APPLICATIONS_SOURCES
-    ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleQtApplicationRunner.h
-    ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindow.cpp
-    ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindowWithButtons.cpp
-    )
-
-  ORTHANC_QT_WRAP_UI(SAMPLE_APPLICATIONS_SOURCES
-    ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindow.ui
-    ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindowWithButtons.ui
-    )
-
-  ORTHANC_QT_WRAP_CPP(SAMPLE_APPLICATIONS_SOURCES
-    ${ORTHANC_STONE_ROOT}/Applications/Qt/QCairoWidget.h
-    ${ORTHANC_STONE_ROOT}/Applications/Qt/QStoneMainWindow.h
-    ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindow.h
-    ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindowWithButtons.h
-    )
-endif()
-
-if (ENABLE_NATIVE)
-  list(APPEND SAMPLE_APPLICATIONS_SOURCES
-    ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleMainNative.cpp
-    )
-
-elseif (ENABLE_WASM)
-
-  list(APPEND SAMPLE_APPLICATIONS_SOURCES
-    ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleMainWasm.cpp
-    ${STONE_WASM_SOURCES}
-    )
-endif()
-
-
-macro(BuildSingleFileSample Target Header Sample)
-  add_executable(${Target}
-    ${ORTHANC_STONE_ROOT}/Applications/Samples/${Header}
-    ${SAMPLE_APPLICATIONS_SOURCES}
-    )
-  set_target_properties(${Target} PROPERTIES COMPILE_DEFINITIONS ORTHANC_STONE_SAMPLE=${Sample})
-  target_link_libraries(${Target} OrthancStone)
-endmacro()
-
-
-if (ENABLE_SDL)
-  #BuildSingleFileSample(OrthancStoneEmpty EmptyApplication.h 1)
-  #BuildSingleFileSample(OrthancStoneTestPattern TestPatternApplication.h 2)
-  BuildSingleFileSample(OrthancStoneSingleFrame SingleFrameApplication.h 3)
-  #BuildSingleFileSample(OrthancStoneSingleVolume SingleVolumeApplication.h 4)
-  #BuildSingleFileSample(OrthancStoneBasicPetCtFusion 5)
-  #BuildSingleFileSample(OrthancStoneSynchronizedSeries 6)
-  #BuildSingleFileSample(OrthancStoneLayoutPetCtFusion 7)
-  BuildSingleFileSample(OrthancStoneSimpleViewerSingleFile SimpleViewerApplicationSingleFile.h 8)  # we keep that one just as a sample before we convert another sample to this pattern
-  BuildSingleFileSample(OrthancStoneSingleFrameEditor SingleFrameEditorApplication.h 9)
-endif()
-  
-##### SimpleViewer sample (Qt and WASM only) #######
-
-if (ENABLE_QT OR ENABLE_WASM)
-
-    if (ENABLE_QT)
-      list(APPEND SIMPLE_VIEWER_APPLICATION_SOURCES
-        ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Qt/SimpleViewerMainWindow.cpp
-        ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Qt/SimpleViewerMainWindow.ui
-        ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Qt/mainQt.cpp
-        )
-
-      ORTHANC_QT_WRAP_UI(SIMPLE_VIEWER_APPLICATION_SOURCES
-        ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Qt/SimpleViewerMainWindow.ui
-        )
-
-      ORTHANC_QT_WRAP_CPP(SIMPLE_VIEWER_APPLICATION_SOURCES
-        ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Qt/SimpleViewerMainWindow.h
-        )
-
-elseif (ENABLE_WASM)
-        list(APPEND SIMPLE_VIEWER_APPLICATION_SOURCES
-            ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Wasm/mainWasm.cpp
-            ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.cpp
-            ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.h
-            ${STONE_WASM_SOURCES}
-          )
-    endif()
-
-    add_executable(OrthancStoneSimpleViewer
-      ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/AppStatus.h
-      ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/MainWidgetInteractor.cpp
-      ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/MainWidgetInteractor.h
-      ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/SimpleViewerApplication.cpp
-      ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/SimpleViewerApplication.h
-      ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/ThumbnailInteractor.cpp
-      ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/ThumbnailInteractor.h
-      ${SIMPLE_VIEWER_APPLICATION_SOURCES}
-      )
-    target_link_libraries(OrthancStoneSimpleViewer OrthancStone)
-
-    BuildSingleFileSample(OrthancStoneSingleFrameEditor SingleFrameEditorApplication.h 9)
-endif()
-
-#####################################################################
-## Build the unit tests
-#####################################################################
-
-if (ENABLE_NATIVE)
-  add_executable(UnitTests
-    ${GOOGLE_TEST_SOURCES}
-    ${ORTHANC_STONE_ROOT}/UnitTestsSources/GenericToolboxTests.cpp
-    ${ORTHANC_STONE_ROOT}/UnitTestsSources/ImageToolboxTests.cpp
-    ${ORTHANC_STONE_ROOT}/UnitTestsSources/PixelTestPatternsTests.cpp
-    ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestCommands.cpp
-    ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestMessageBroker.cpp
-    ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestStrategy.cpp
-    ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestStructureSet.cpp
-    ${ORTHANC_STONE_ROOT}/UnitTestsSources/UnitTestsMain.cpp
-    )
-
-  target_link_libraries(UnitTests OrthancStone)
-
-  add_custom_command(
-    TARGET UnitTests
-    POST_BUILD
-    COMMAND ${CMAKE_COMMAND} -E copy
-      "${ORTHANC_STONE_ROOT}/UnitTestsSources/72c773ac-5059f2c4-2e6a9120-4fd4bca1-45701661.json" 
-      "$<TARGET_FILE_DIR:UnitTests>/72c773ac-5059f2c4-2e6a9120-4fd4bca1-45701661.json"
-    )
-
-endif()
-
-#####################################################################
-## Generate the documentation if Doxygen is present
-#####################################################################
-
-find_package(Doxygen)
-if (DOXYGEN_FOUND)
-  configure_file(
-    ${ORTHANC_STONE_ROOT}/Resources/OrthancStone.doxygen
-    ${CMAKE_CURRENT_BINARY_DIR}/OrthancStone.doxygen
-    @ONLY)
-
-  add_custom_target(doc
-    ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/OrthancStone.doxygen
-    COMMENT "Generating documentation with Doxygen" VERBATIM
-    )
-else()
-  message("Doxygen not found. The documentation will not be built.")
-endif()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Applications/Samples/Deprecated/CMakeLists.txt	Tue Apr 07 14:31:28 2020 +0200
@@ -0,0 +1,292 @@
+# Usage (Linux):
+# to build the WASM samples
+# source ~/Downloads/emsdk/emsdk_env.sh && cmake -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DSTONE_SOURCES_DIR=$currentDir/../../../orthanc-stone -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT=$currentDir/../../../orthanc -DALLOW_DOWNLOADS=ON .. -DENABLE_WASM=ON
+# to build the Qt samples
+
+cmake_minimum_required(VERSION 2.8.3)
+project(OrthancStone)
+
+include(../../Resources/CMake/OrthancStoneParameters.cmake)
+
+set(ENABLE_STONE_DEPRECATED ON)  # Need deprecated classes for these samples
+set(EMSCRIPTEN_SET_LLVM_WASM_BACKEND ON)
+
+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
+  )
+
+if (OPENSSL_NO_CAPIENG)
+add_definitions(-DOPENSSL_NO_CAPIENG=1)
+endif()
+
+
+# the following block has been borrowed from orthanc/**/Compiler.cmake
+if (MSVC_MULTIPLE_PROCESSES)
+# "If you omit the processMax argument in the /MP option, the
+# compiler obtains the number of effective processors from the
+# operating system, and then creates one process per effective
+# processor"
+# https://blog.kitware.com/cmake-building-with-all-your-cores/
+# https://docs.microsoft.com/en-us/cpp/build/reference/mp-build-with-multiple-processes
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
+endif()
+
+#set(ENABLE_DCMTK ON)
+
+set(ENABLE_SDL OFF CACHE BOOL "Target SDL Native application")
+set(ENABLE_QT OFF CACHE BOOL "Target Qt Native application")
+set(ENABLE_WASM OFF CACHE BOOL "Target WASM application")
+
+if (ENABLE_WASM)
+  #####################################################################
+  ## Configuration of the Emscripten compiler for WebAssembly target
+  #####################################################################
+
+  set(WASM_FLAGS "-s WASM=1")
+  set(WASM_FLAGS "${WASM_FLAGS} -s STRICT=1") # drops support for all deprecated build options
+  set(WASM_FLAGS "${WASM_FLAGS} -s FILESYSTEM=1") # if we don't include it, gen_uuid.c fails to build because srand, getpid(), ... are not defined
+  set(WASM_FLAGS "${WASM_FLAGS} -s DISABLE_EXCEPTION_CATCHING=0") # actually enable exception catching 
+  set(WASM_FLAGS "${WASM_FLAGS} -s ERROR_ON_MISSING_LIBRARIES=1")
+  
+  if (CMAKE_BUILD_TYPE MATCHES DEBUG)
+    set(WASM_FLAGS "${WASM_FLAGS} -g4") # generate debug information
+    set(WASM_FLAGS "${WASM_FLAGS} -s ASSERTIONS=2") # more runtime checks
+  else()
+    set(WASM_FLAGS "${WASM_FLAGS} -Os") # optimize for web (speed and size)
+  endif()
+
+  set(WASM_MODULE_NAME "StoneFrameworkModule" CACHE STRING "Name of the WebAssembly module")
+
+  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} ${WASM_FLAGS}")  # not always clear which flags are for the compiler and which one are for the linker -> pass them all to the linker too
+  # set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Applications/Samples/samples-library.js")
+  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmWebService.js")
+  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/WasmDelayedCallExecutor.js")
+  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --js-library ${STONE_SOURCES_DIR}/Platforms/Wasm/default-library.js")
+  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 EXPORT_NAME='\"${WASM_MODULE_NAME}\"'")
+  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1")
+  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s TOTAL_MEMORY=536870912")
+  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s TOTAL_STACK=128000000")
+
+  add_definitions(-DORTHANC_ENABLE_WASM=1)
+  set(ORTHANC_SANDBOXED ON)
+
+elseif (ENABLE_QT OR ENABLE_SDL)
+
+  set(ENABLE_NATIVE ON)
+  set(ORTHANC_SANDBOXED OFF)
+  set(ENABLE_CRYPTO_OPTIONS ON)
+  set(ENABLE_GOOGLE_TEST ON)
+  set(ENABLE_WEB_CLIENT ON)
+
+else()
+  set(ENABLE_NATIVE ON)
+  set(ENABLE_OPENGL OFF)
+  
+endif()
+
+
+#####################################################################
+## Configuration for Orthanc
+#####################################################################
+
+# include(../../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.4.1")
+  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\"")
+
+add_definitions(
+  -DORTHANC_ENABLE_LOGGING_PLUGIN=0
+  )
+
+
+#####################################################################
+## Build a static library containing the Orthanc Stone framework
+#####################################################################
+
+
+LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options)
+
+include(../../Resources/CMake/OrthancStoneConfiguration.cmake)
+
+add_library(OrthancStone STATIC
+  ${ORTHANC_STONE_SOURCES}
+  )
+
+#####################################################################
+## Build all the sample applications
+#####################################################################
+
+include_directories(${ORTHANC_STONE_ROOT})
+
+# files common to all samples
+list(APPEND SAMPLE_APPLICATIONS_SOURCES
+  ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleInteractor.h
+  ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleApplicationBase.h
+  )
+
+if (ENABLE_QT)
+  list(APPEND SAMPLE_APPLICATIONS_SOURCES
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleQtApplicationRunner.h
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindow.cpp
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindowWithButtons.cpp
+    )
+
+  ORTHANC_QT_WRAP_UI(SAMPLE_APPLICATIONS_SOURCES
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindow.ui
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindowWithButtons.ui
+    )
+
+  ORTHANC_QT_WRAP_CPP(SAMPLE_APPLICATIONS_SOURCES
+    ${ORTHANC_STONE_ROOT}/Applications/Qt/QCairoWidget.h
+    ${ORTHANC_STONE_ROOT}/Applications/Qt/QStoneMainWindow.h
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindow.h
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/Qt/SampleMainWindowWithButtons.h
+    )
+endif()
+
+if (ENABLE_NATIVE)
+  list(APPEND SAMPLE_APPLICATIONS_SOURCES
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleMainNative.cpp
+    )
+
+elseif (ENABLE_WASM)
+
+  list(APPEND SAMPLE_APPLICATIONS_SOURCES
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/SampleMainWasm.cpp
+    ${STONE_WASM_SOURCES}
+    )
+endif()
+
+
+macro(BuildSingleFileSample Target Header Sample)
+  add_executable(${Target}
+    ${ORTHANC_STONE_ROOT}/Applications/Samples/${Header}
+    ${SAMPLE_APPLICATIONS_SOURCES}
+    )
+  set_target_properties(${Target} PROPERTIES COMPILE_DEFINITIONS ORTHANC_STONE_SAMPLE=${Sample})
+  target_link_libraries(${Target} OrthancStone)
+endmacro()
+
+
+if (ENABLE_SDL)
+  #BuildSingleFileSample(OrthancStoneEmpty EmptyApplication.h 1)
+  #BuildSingleFileSample(OrthancStoneTestPattern TestPatternApplication.h 2)
+  BuildSingleFileSample(OrthancStoneSingleFrame SingleFrameApplication.h 3)
+  #BuildSingleFileSample(OrthancStoneSingleVolume SingleVolumeApplication.h 4)
+  #BuildSingleFileSample(OrthancStoneBasicPetCtFusion 5)
+  #BuildSingleFileSample(OrthancStoneSynchronizedSeries 6)
+  #BuildSingleFileSample(OrthancStoneLayoutPetCtFusion 7)
+  BuildSingleFileSample(OrthancStoneSimpleViewerSingleFile SimpleViewerApplicationSingleFile.h 8)  # we keep that one just as a sample before we convert another sample to this pattern
+  BuildSingleFileSample(OrthancStoneSingleFrameEditor SingleFrameEditorApplication.h 9)
+endif()
+  
+##### SimpleViewer sample (Qt and WASM only) #######
+
+if (ENABLE_QT OR ENABLE_WASM)
+
+    if (ENABLE_QT)
+      list(APPEND SIMPLE_VIEWER_APPLICATION_SOURCES
+        ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Qt/SimpleViewerMainWindow.cpp
+        ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Qt/SimpleViewerMainWindow.ui
+        ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Qt/mainQt.cpp
+        )
+
+      ORTHANC_QT_WRAP_UI(SIMPLE_VIEWER_APPLICATION_SOURCES
+        ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Qt/SimpleViewerMainWindow.ui
+        )
+
+      ORTHANC_QT_WRAP_CPP(SIMPLE_VIEWER_APPLICATION_SOURCES
+        ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Qt/SimpleViewerMainWindow.h
+        )
+
+elseif (ENABLE_WASM)
+        list(APPEND SIMPLE_VIEWER_APPLICATION_SOURCES
+            ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Wasm/mainWasm.cpp
+            ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.cpp
+            ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/Wasm/SimpleViewerWasmApplicationAdapter.h
+            ${STONE_WASM_SOURCES}
+          )
+    endif()
+
+    add_executable(OrthancStoneSimpleViewer
+      ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/AppStatus.h
+      ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/MainWidgetInteractor.cpp
+      ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/MainWidgetInteractor.h
+      ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/SimpleViewerApplication.cpp
+      ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/SimpleViewerApplication.h
+      ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/ThumbnailInteractor.cpp
+      ${ORTHANC_STONE_ROOT}/Applications/Samples/SimpleViewer/ThumbnailInteractor.h
+      ${SIMPLE_VIEWER_APPLICATION_SOURCES}
+      )
+    target_link_libraries(OrthancStoneSimpleViewer OrthancStone)
+
+    BuildSingleFileSample(OrthancStoneSingleFrameEditor SingleFrameEditorApplication.h 9)
+endif()
+
+#####################################################################
+## Build the unit tests
+#####################################################################
+
+if (ENABLE_NATIVE)
+  add_executable(UnitTests
+    ${GOOGLE_TEST_SOURCES}
+    ${ORTHANC_STONE_ROOT}/UnitTestsSources/GenericToolboxTests.cpp
+    ${ORTHANC_STONE_ROOT}/UnitTestsSources/ImageToolboxTests.cpp
+    ${ORTHANC_STONE_ROOT}/UnitTestsSources/PixelTestPatternsTests.cpp
+    ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestCommands.cpp
+    ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestMessageBroker.cpp
+    ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestStrategy.cpp
+    ${ORTHANC_STONE_ROOT}/UnitTestsSources/TestStructureSet.cpp
+    ${ORTHANC_STONE_ROOT}/UnitTestsSources/UnitTestsMain.cpp
+    )
+
+  target_link_libraries(UnitTests OrthancStone)
+
+  add_custom_command(
+    TARGET UnitTests
+    POST_BUILD
+    COMMAND ${CMAKE_COMMAND} -E copy
+      "${ORTHANC_STONE_ROOT}/UnitTestsSources/72c773ac-5059f2c4-2e6a9120-4fd4bca1-45701661.json" 
+      "$<TARGET_FILE_DIR:UnitTests>/72c773ac-5059f2c4-2e6a9120-4fd4bca1-45701661.json"
+    )
+
+endif()
+
+#####################################################################
+## Generate the documentation if Doxygen is present
+#####################################################################
+
+find_package(Doxygen)
+if (DOXYGEN_FOUND)
+  configure_file(
+    ${ORTHANC_STONE_ROOT}/Resources/OrthancStone.doxygen
+    ${CMAKE_CURRENT_BINARY_DIR}/OrthancStone.doxygen
+    @ONLY)
+
+  add_custom_target(doc
+    ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/OrthancStone.doxygen
+    COMMENT "Generating documentation with Doxygen" VERBATIM
+    )
+else()
+  message("Doxygen not found. The documentation will not be built.")
+endif()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/Deprecated/MultiPlatform/BasicScene/BasicScene.cpp	Tue Apr 07 14:31:28 2020 +0200
@@ -0,0 +1,275 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 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 "BasicScene.h"
+
+// From Stone
+#include "Framework/Scene2D/Scene2D.h"
+#include "Framework/Scene2D/ColorTextureSceneLayer.h"
+#include "Framework/Scene2D/PolylineSceneLayer.h"
+#include "Framework/Scene2D/TextSceneLayer.h"
+
+#include "Framework/Scene2D/PanSceneTracker.h"
+#include "Framework/Scene2D/ZoomSceneTracker.h"
+#include "Framework/Scene2D/RotateSceneTracker.h"
+
+#include "Framework/Scene2D/CairoCompositor.h"
+
+// From Orthanc framework
+#include <Core/Images/Image.h>
+#include <Core/Images/ImageProcessing.h>
+#include <Core/Images/PngWriter.h>
+
+using namespace OrthancStone;
+
+const unsigned int BASIC_SCENE_FONT_SIZE = 32;
+const int BASIC_SCENE_LAYER_POSITION = 150;
+
+void PrepareScene(Scene2D& scene)
+{
+  //Scene2D& scene(*controller->GetScene());
+  // Texture of 2x2 size
+  {
+    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::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i));
+    l->SetOrigin(-3, 2);
+    l->SetPixelSpacing(1.5, 1);
+    l->SetAngle(20.0 / 180.0 * 3.14);
+    scene.SetLayer(14, l.release());
+  }
+
+  // Texture of 1x1 size
+  {
+    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::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i));
+    l->SetOrigin(-2, 1);
+    l->SetAngle(20.0 / 180.0 * 3.14);
+    scene.SetLayer(13, l.release());
+  }
+
+  // Some lines
+  {
+    std::unique_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, 255, 0, 0);
+
+    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, 0, 255, 0);
+
+    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, 0, 0, 255);
+
+    //    layer->SetColor(0,255, 255);
+    scene.SetLayer(50, layer.release());
+  }
+
+  // Some text
+  {
+    std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer);
+    layer->SetText("Hello");
+    scene.SetLayer(100, layer.release());
+  }
+}
+
+#if ORTHANC_SANDBOXED == 0
+void TakeScreenshot(const std::string& target,
+                    const OrthancStone::Scene2D& scene,
+                    unsigned int canvasWidth,
+                    unsigned int canvasHeight)
+{
+  using namespace OrthancStone;
+  // Take a screenshot, then save it as PNG file
+  CairoCompositor compositor(scene, canvasWidth, canvasHeight);
+  compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, BASIC_SCENE_FONT_SIZE, Orthanc::Encoding_Latin1);
+  compositor.Refresh();
+
+  Orthanc::ImageAccessor canvas;
+  compositor.GetCanvas().GetReadOnlyAccessor(canvas);
+
+  Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false);
+  Orthanc::ImageProcessing::Convert(png, canvas);
+
+  Orthanc::PngWriter writer;
+  writer.WriteToFile(target, png);
+}
+#endif
+
+void ShowCursorInfo(Scene2D& scene, const PointerEvent& pointerEvent)
+{
+  ScenePoint2D p = pointerEvent.GetMainPosition().Apply(scene.GetCanvasToSceneTransform());
+
+  char buf[64];
+  sprintf(buf, "(%0.02f,%0.02f)", p.GetX(), p.GetY());
+
+  if (scene.HasLayer(BASIC_SCENE_LAYER_POSITION))
+  {
+    TextSceneLayer& layer =
+        dynamic_cast<TextSceneLayer&>(scene.GetLayer(BASIC_SCENE_LAYER_POSITION));
+    layer.SetText(buf);
+    layer.SetPosition(p.GetX(), p.GetY());
+  }
+  else
+  {
+    std::unique_ptr<TextSceneLayer>
+        layer(new TextSceneLayer);
+    layer->SetColor(0, 255, 0);
+    layer->SetText(buf);
+    layer->SetBorder(20);
+    layer->SetAnchor(BitmapAnchor_BottomCenter);
+    layer->SetPosition(p.GetX(), p.GetY());
+    scene.SetLayer(BASIC_SCENE_LAYER_POSITION, layer.release());
+  }
+}
+
+
+
+bool BasicScene2DInteractor::OnMouseEvent(const GuiAdapterMouseEvent& event, const PointerEvent& pointerEvent)
+{
+  if (currentTracker_.get() != NULL)
+  {
+    switch (event.type)
+    {
+    case GUIADAPTER_EVENT_MOUSEUP:
+    {
+      currentTracker_->PointerUp(pointerEvent);
+      if (!currentTracker_->IsAlive())
+      {
+        currentTracker_.reset();
+      }
+    };break;
+    case GUIADAPTER_EVENT_MOUSEMOVE:
+    {
+      currentTracker_->PointerMove(pointerEvent);
+    };break;
+    default:
+      return false;
+    }
+    return true;
+  }
+  else if (event.type == GUIADAPTER_EVENT_MOUSEDOWN)
+  {
+    if (event.button == GUIADAPTER_MOUSEBUTTON_LEFT)
+    {
+      currentTracker_.reset(new RotateSceneTracker(viewportController_, pointerEvent));
+    }
+    else if (event.button == GUIADAPTER_MOUSEBUTTON_MIDDLE)
+    {
+      currentTracker_.reset(new PanSceneTracker(viewportController_, pointerEvent));
+    }
+    else if (event.button == GUIADAPTER_MOUSEBUTTON_RIGHT)
+    {
+      currentTracker_.reset(new ZoomSceneTracker(viewportController_, pointerEvent, viewportController_->GetViewport().GetCanvasHeight()));
+    }
+  }
+  else if (event.type == GUIADAPTER_EVENT_MOUSEMOVE)
+  {
+    if (showCursorInfo_)
+    {
+      Scene2D& scene(viewportController_->GetScene());
+      ShowCursorInfo(scene, pointerEvent);
+    }
+    return true;
+  }
+  return false;
+}
+
+bool BasicScene2DInteractor::OnKeyboardEvent(const GuiAdapterKeyboardEvent& guiEvent)
+{
+  if (guiEvent.type == GUIADAPTER_EVENT_KEYDOWN)
+  {
+    switch (guiEvent.sym[0])
+    {
+    case 's':
+    {
+      //viewportController_->FitContent(viewportController_->GetViewport().GetCanvasWidth(), viewportController_->GetViewport().GetCanvasHeight());
+      viewportController_->FitContent();
+      return true;
+    };
+#if ORTHANC_SANDBOXED == 0
+    case 'c':
+    {
+      Scene2D& scene(viewportController_->GetScene());
+      TakeScreenshot("screenshot.png", scene, viewportController_->GetViewport().GetCanvasWidth(), viewportController_->GetViewport().GetCanvasHeight());
+      return true;
+    }
+#endif
+    case 'd':
+    {
+      showCursorInfo_ = !showCursorInfo_;
+      if (!showCursorInfo_)
+      {
+        Scene2D& scene(viewportController_->GetScene());
+        scene.DeleteLayer(BASIC_SCENE_LAYER_POSITION);
+      }
+
+      return true;
+    }
+    }
+  }
+  return false;
+}
+
+bool BasicScene2DInteractor::OnWheelEvent(const GuiAdapterWheelEvent& guiEvent)
+{
+  return false;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/Deprecated/MultiPlatform/BasicScene/BasicScene.h	Tue Apr 07 14:31:28 2020 +0200
@@ -0,0 +1,55 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 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
+
+#include <boost/shared_ptr.hpp>
+#include "Framework/Scene2DViewport/ViewportController.h"
+#include "Framework/Scene2D/Scene2D.h"
+
+extern const unsigned int BASIC_SCENE_FONT_SIZE;
+extern const int BASIC_SCENE_LAYER_POSITION;
+
+extern void PrepareScene(OrthancStone::Scene2D& scene);
+extern void TakeScreenshot(const std::string& target,
+                           const OrthancStone::Scene2D& scene,
+                           unsigned int canvasWidth,
+                           unsigned int canvasHeight);
+
+
+#include "Applications/Generic/Scene2DInteractor.h"
+#include "Framework/Scene2DViewport/IFlexiblePointerTracker.h"
+
+
+class BasicScene2DInteractor : public OrthancStone::Scene2DInteractor
+{
+  boost::shared_ptr<OrthancStone::IFlexiblePointerTracker>  currentTracker_;
+  bool                                                      showCursorInfo_;
+public:
+  BasicScene2DInteractor(boost::shared_ptr<OrthancStone::ViewportController> viewportController) :
+    Scene2DInteractor(viewportController),
+    showCursorInfo_(false)
+  {}
+
+  virtual bool OnMouseEvent(const OrthancStone::GuiAdapterMouseEvent& event, const OrthancStone::PointerEvent& pointerEvent) override;
+  virtual bool OnKeyboardEvent(const OrthancStone::GuiAdapterKeyboardEvent& guiEvent) override;
+  virtual bool OnWheelEvent(const OrthancStone::GuiAdapterWheelEvent& guiEvent) override;
+};
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/Deprecated/MultiPlatform/BasicScene/mainQt.cpp	Tue Apr 07 14:31:28 2020 +0200
@@ -0,0 +1,103 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 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/>.
+ **/
+
+#define GLEW_STATIC 1
+// From Stone
+#include "../../Framework/OpenGL/OpenGLIncludes.h"
+#include "../../Applications/Sdl/SdlWindow.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/Scene2DViewport/ViewportController.h"
+#include "../../Framework/Scene2DViewport/UndoStack.h"
+
+#include "../../Framework/StoneInitialization.h"
+#include "../../Framework/Messages/MessageBroker.h"
+
+// From Orthanc framework
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+#include <Core/Images/Image.h>
+#include <Core/Images/ImageProcessing.h>
+#include <Core/Images/PngWriter.h>
+
+#include <boost/make_shared.hpp>
+#include <boost/ref.hpp>
+#include "EmbeddedResources.h"
+
+#include <stdio.h>
+#include <QDebug>
+#include <QWindow>
+
+#include "BasicScene.h"
+
+
+using namespace OrthancStone;
+
+
+
+static void GLAPIENTRY OpenGLMessageCallback(GLenum source,
+                                             GLenum type,
+                                             GLuint id,
+                                             GLenum severity,
+                                             GLsizei length,
+                                             const GLchar* message,
+                                             const void* userParam )
+{
+  if (severity != GL_DEBUG_SEVERITY_NOTIFICATION)
+  {
+    fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
+            ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ),
+            type, severity, message );
+  }
+}
+
+extern void InitGL();
+
+#include <QApplication>
+#include "BasicSceneWindow.h"
+
+int main(int argc, char* argv[])
+{
+  QApplication a(argc, argv);
+
+  OrthancStone::Samples::BasicSceneWindow window;
+  window.show();
+  window.GetOpenGlWidget().Init();
+
+  MessageBroker broker;
+  boost::shared_ptr<UndoStack> undoStack(new UndoStack);
+  boost::shared_ptr<ViewportController> controller = boost::make_shared<ViewportController>(undoStack, boost::ref(broker), window.GetOpenGlWidget());
+  PrepareScene(controller->GetScene());
+
+  window.GetOpenGlWidget().GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT,
+                     BASIC_SCENE_FONT_SIZE, Orthanc::Encoding_Latin1);
+
+  boost::shared_ptr<OrthancStone::Scene2DInteractor> interactor(new BasicScene2DInteractor(controller));
+  window.GetOpenGlWidget().SetInteractor(interactor);
+
+  controller->FitContent();
+
+  return a.exec();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/Deprecated/MultiPlatform/BasicScene/mainSdl.cpp	Tue Apr 07 14:31:28 2020 +0200
@@ -0,0 +1,199 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 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/>.
+ **/
+
+
+// From Stone
+#include "Framework/Viewport/SdlViewport.h"
+#include "Framework/Scene2D/OpenGLCompositor.h"
+#include "Framework/Scene2DViewport/UndoStack.h"
+#include "Framework/StoneInitialization.h"
+#include "Framework/Messages/MessageBroker.h"
+
+// From Orthanc framework
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+
+#include <boost/make_shared.hpp>
+#include <boost/ref.hpp>
+
+#include <SDL.h>
+#include <stdio.h>
+
+
+#include "BasicScene.h"
+
+using namespace OrthancStone;
+
+boost::shared_ptr<BasicScene2DInteractor> interactor;
+
+void HandleApplicationEvent(boost::shared_ptr<OrthancStone::ViewportController> controller,
+                            const SDL_Event& event)
+{
+  using namespace OrthancStone;
+  Scene2D& scene(controller->GetScene());
+  if (event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP || event.type == SDL_MOUSEMOTION)
+  {
+    // TODO: this code is copy/pasted from GuiAdapter::Run() -> find the right place
+    int scancodeCount = 0;
+    const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount);
+    bool ctrlPressed(false);
+    bool shiftPressed(false);
+    bool altPressed(false);
+
+    if (SDL_SCANCODE_LCTRL < scancodeCount && keyboardState[SDL_SCANCODE_LCTRL])
+      ctrlPressed = true;
+    if (SDL_SCANCODE_RCTRL < scancodeCount && keyboardState[SDL_SCANCODE_RCTRL])
+      ctrlPressed = true;
+    if (SDL_SCANCODE_LSHIFT < scancodeCount && keyboardState[SDL_SCANCODE_LSHIFT])
+      shiftPressed = true;
+    if (SDL_SCANCODE_RSHIFT < scancodeCount && keyboardState[SDL_SCANCODE_RSHIFT])
+      shiftPressed = true;
+    if (SDL_SCANCODE_LALT < scancodeCount && keyboardState[SDL_SCANCODE_LALT])
+      altPressed = true;
+
+    GuiAdapterMouseEvent guiEvent;
+    ConvertFromPlatform(guiEvent, ctrlPressed, shiftPressed, altPressed, event);
+    PointerEvent pointerEvent;
+    pointerEvent.AddPosition(controller->GetViewport().GetPixelCenterCoordinates(event.button.x, event.button.y));
+
+    interactor->OnMouseEvent(guiEvent, pointerEvent);
+    return;
+  }
+  else if ((event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) && event.key.repeat == 0  /* Ignore key bounce */)
+  {
+    GuiAdapterKeyboardEvent guiEvent;
+    ConvertFromPlatform(guiEvent, event);
+
+    interactor->OnKeyboardEvent(guiEvent);
+  }
+
+}
+
+
+static void GLAPIENTRY
+OpenGLMessageCallback(GLenum source,
+                      GLenum type,
+                      GLuint id,
+                      GLenum severity,
+                      GLsizei length,
+                      const GLchar* message,
+                      const void* userParam )
+{
+  if (severity != GL_DEBUG_SEVERITY_NOTIFICATION)
+  {
+    fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
+            ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ),
+            type, severity, message );
+  }
+}
+
+
+void Run(boost::shared_ptr<OrthancStone::ViewportController> controller)
+{
+  SdlViewport& sdlViewport = dynamic_cast<SdlViewport&>(controller->GetViewport());
+
+  glEnable(GL_DEBUG_OUTPUT);
+  glDebugMessageCallback(OpenGLMessageCallback, 0);
+
+  controller->GetViewport().GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT,
+                     BASIC_SCENE_FONT_SIZE, Orthanc::Encoding_Latin1);
+
+  controller->GetViewport().Refresh();
+  controller->FitContent();
+
+
+  bool stop = false;
+  while (!stop)
+  {
+    controller->GetViewport().Refresh();
+
+    SDL_Event event;
+    while (!stop &&
+           SDL_PollEvent(&event))
+    {
+      if (event.type == SDL_QUIT)
+      {
+        stop = true;
+        break;
+      }
+      else if (event.type == SDL_WINDOWEVENT &&
+               event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
+      {
+        sdlViewport.UpdateSize(event.window.data1, event.window.data2);
+      }
+      else if (event.type == SDL_KEYDOWN &&
+               event.key.repeat == 0 /* Ignore key bounce */)
+      {
+        switch (event.key.keysym.sym)
+        {
+          case SDLK_f:
+            sdlViewport.GetWindow().ToggleMaximize();
+            break;
+              
+          case SDLK_q:
+            stop = true;
+            break;
+
+          default:
+            break;
+        }
+      }
+      
+      HandleApplicationEvent(controller, event);
+    }
+
+    SDL_Delay(1);
+  }
+  interactor.reset();
+}
+
+
+
+
+/**
+ * IMPORTANT: The full arguments to "main()" are needed for SDL on
+ * Windows. Otherwise, one gets the linking error "undefined reference
+ * to `SDL_main'". https://wiki.libsdl.org/FAQWindows
+ **/
+int main(int argc, char* argv[])
+{
+  using namespace OrthancStone;
+  StoneInitialize();
+  Orthanc::Logging::EnableInfoLevel(true);
+
+  try
+  {
+    SdlOpenGLViewport viewport("Hello", 1024, 768);
+    MessageBroker broker;
+    boost::shared_ptr<UndoStack> undoStack(new UndoStack);
+    boost::shared_ptr<ViewportController> controller = boost::make_shared<ViewportController>(undoStack, boost::ref(broker), boost::ref(viewport));
+    interactor.reset(new BasicScene2DInteractor(controller));
+    PrepareScene(controller->GetScene());
+    Run(controller);
+  }
+  catch (Orthanc::OrthancException& e)
+  {
+    LOG(ERROR) << "EXCEPTION: " << e.What();
+  }
+
+  StoneFinalize();
+
+  return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/Deprecated/Qt/BasicSceneWindow.cpp	Tue Apr 07 14:31:28 2020 +0200
@@ -0,0 +1,53 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 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 "../../Framework/OpenGL/OpenGLIncludes.h"
+#include "BasicSceneWindow.h"
+
+/**
+ * Don't use "ui_MainWindow.h" instead of <ui_MainWindow.h> below, as
+ * this makes CMake unable to detect when the UI file changes.
+ **/
+#include <ui_BasicSceneWindow.h>
+#include "../../Applications/Samples/SampleApplicationBase.h"
+
+namespace OrthancStone
+{
+  namespace Samples
+  {
+
+    BasicSceneWindow::BasicSceneWindow(
+      QWidget *parent) :
+      ui_(new Ui::BasicSceneWindow)
+    {
+      ui_->setupUi(this);
+    }
+
+    BasicSceneWindow::~BasicSceneWindow()
+    {
+      delete ui_;
+    }
+
+    QStoneOpenGlWidget& BasicSceneWindow::GetOpenGlWidget()
+    {
+      return *(ui_->centralWidget);
+    }
+
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/Deprecated/Qt/BasicSceneWindow.h	Tue Apr 07 14:31:28 2020 +0200
@@ -0,0 +1,53 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 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
+#include <QMainWindow>
+#include <QStoneOpenGlWidget.h>
+// #include "../../Qt/QCairoWidget.h"
+// #include "../../Qt/QStoneMainWindow.h"
+
+namespace Ui 
+{
+  class BasicSceneWindow;
+}
+
+namespace OrthancStone
+{
+  namespace Samples
+  {
+
+    //class SampleSingleCanvasApplicationBase;
+
+    class BasicSceneWindow : public QMainWindow
+    {
+      Q_OBJECT
+
+    private:
+      Ui::BasicSceneWindow*   ui_;
+      //SampleSingleCanvasApplicationBase&  stoneSampleApplication_;
+
+    public:
+      explicit BasicSceneWindow(QWidget *parent = 0);
+      ~BasicSceneWindow();
+
+      QStoneOpenGlWidget& GetOpenGlWidget();
+    };
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/Deprecated/Qt/BasicSceneWindow.ui	Tue Apr 07 14:31:28 2020 +0200
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>BasicSceneWindow</class>
+ <widget class="QMainWindow" name="BasicSceneWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>903</width>
+    <height>634</height>
+   </rect>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>500</width>
+    <height>300</height>
+   </size>
+  </property>
+  <property name="baseSize">
+   <size>
+    <width>500</width>
+    <height>300</height>
+   </size>
+  </property>
+  <property name="windowTitle">
+   <string>Stone of Orthanc</string>
+  </property>
+  <property name="layoutDirection">
+   <enum>Qt::LeftToRight</enum>
+  </property>
+  <widget class="QWidget" name="mainWidget">
+   <property name="sizePolicy">
+    <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+     <horstretch>0</horstretch>
+     <verstretch>0</verstretch>
+    </sizepolicy>
+   </property>
+   <property name="layoutDirection">
+    <enum>Qt::LeftToRight</enum>
+   </property>
+   <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0">
+    <property name="sizeConstraint">
+     <enum>QLayout::SetDefaultConstraint</enum>
+    </property>
+    <item>
+     <widget class="OrthancStone::QStoneOpenGlWidget" name="centralWidget" native="true">
+      <property name="minimumSize">
+       <size>
+        <width>0</width>
+        <height>500</height>
+       </size>
+      </property>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="menubar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>903</width>
+     <height>21</height>
+    </rect>
+   </property>
+   <widget class="QMenu" name="menuTest">
+    <property name="title">
+     <string>Test</string>
+    </property>
+   </widget>
+   <addaction name="menuTest"/>
+  </widget>
+  <widget class="QStatusBar" name="statusbar"/>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>QStoneOpenGlWidget</class>
+   <extends>QWidget</extends>
+   <header location="global">QStoneOpenGlWidget.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/Deprecated/Qt/CMakeLists.txt	Tue Apr 07 14:31:28 2020 +0200
@@ -0,0 +1,83 @@
+cmake_minimum_required(VERSION 2.8.3)
+
+#####################################################################
+## 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(ENABLE_QT ON)
+SET(ENABLE_SDL OFF)
+SET(ENABLE_WEB_CLIENT ON)
+SET(ORTHANC_SANDBOXED OFF)
+LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options)
+
+include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneConfiguration.cmake)
+
+add_definitions(
+  -DORTHANC_ENABLE_LOGGING_PLUGIN=0
+  )
+#####################################################################
+## Build the samples
+#####################################################################
+
+add_library(OrthancStone STATIC
+  ${ORTHANC_STONE_SOURCES}
+  )
+
+list(APPEND BASIC_SCENE_APPLICATIONS_SOURCES
+  BasicSceneWindow.cpp
+  )
+
+ORTHANC_QT_WRAP_UI(BASIC_SCENE_APPLICATIONS_SOURCES
+  BasicSceneWindow.ui
+  )
+
+ORTHANC_QT_WRAP_CPP(BASIC_SCENE_APPLICATIONS_SOURCES
+  BasicSceneWindow.h
+  QStoneOpenGlWidget.h
+  )
+
+add_executable(MpBasicScene
+  ${CMAKE_CURRENT_LIST_DIR}/../MultiPlatform/BasicScene/BasicScene.h
+  ${CMAKE_CURRENT_LIST_DIR}/../MultiPlatform/BasicScene/BasicScene.cpp
+  ${CMAKE_CURRENT_LIST_DIR}/../MultiPlatform/BasicScene/mainQt.cpp
+  QStoneOpenGlWidget.cpp
+  ${BASIC_SCENE_APPLICATIONS_SOURCES}
+  )
+
+target_include_directories(MpBasicScene PUBLIC ${CMAKE_SOURCE_DIR} ${ORTHANC_STONE_ROOT})
+target_link_libraries(MpBasicScene OrthancStone)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/Deprecated/Qt/QStoneOpenGlWidget.cpp	Tue Apr 07 14:31:28 2020 +0200
@@ -0,0 +1,171 @@
+#include "../../Framework/OpenGL/OpenGLIncludes.h"
+#include "QStoneOpenGlWidget.h"
+
+#include <QMouseEvent>
+
+using namespace OrthancStone;
+
+void QStoneOpenGlWidget::initializeGL()
+{
+  glewInit();
+}
+
+void QStoneOpenGlWidget::MakeCurrent()
+{
+  this->makeCurrent();
+}
+
+void QStoneOpenGlWidget::resizeGL(int w, int h)
+{
+
+}
+
+void QStoneOpenGlWidget::paintGL()
+{
+  if (compositor_)
+  {
+    compositor_->Refresh();
+  }
+  doneCurrent();
+}
+
+void ConvertFromPlatform(
+    OrthancStone::GuiAdapterMouseEvent& guiEvent,
+    PointerEvent& pointerEvent,
+    const QMouseEvent& qtEvent,
+    const IViewport& viewport)
+{
+  guiEvent.targetX = qtEvent.x();
+  guiEvent.targetY = qtEvent.y();
+  pointerEvent.AddPosition(viewport.GetPixelCenterCoordinates(guiEvent.targetX, guiEvent.targetY));
+
+  switch (qtEvent.button())
+  {
+  case Qt::LeftButton: guiEvent.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_LEFT; break;
+  case Qt::MiddleButton: guiEvent.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_MIDDLE; break;
+  case Qt::RightButton: guiEvent.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_RIGHT; break;
+  default:
+    guiEvent.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_LEFT;
+  }
+
+  if (qtEvent.modifiers().testFlag(Qt::ShiftModifier))
+  {
+    guiEvent.shiftKey = true;
+  }
+  if (qtEvent.modifiers().testFlag(Qt::ControlModifier))
+  {
+    guiEvent.ctrlKey = true;
+  }
+  if (qtEvent.modifiers().testFlag(Qt::AltModifier))
+  {
+    guiEvent.altKey = true;
+  }
+}
+
+void QStoneOpenGlWidget::mouseEvent(QMouseEvent* qtEvent, OrthancStone::GuiAdapterHidEventType guiEventType)
+{
+  OrthancStone::GuiAdapterMouseEvent guiEvent;
+  PointerEvent pointerEvent;
+  ConvertFromPlatform(guiEvent, pointerEvent, *qtEvent, *this);
+  guiEvent.type = guiEventType;
+
+  if (sceneInteractor_.get() != NULL && compositor_.get() != NULL)
+  {
+    sceneInteractor_->OnMouseEvent(guiEvent, pointerEvent);
+  }
+
+  // force redraw of the OpenGL widget
+  update();
+}
+
+void QStoneOpenGlWidget::mousePressEvent(QMouseEvent* qtEvent)
+{
+  mouseEvent(qtEvent, GUIADAPTER_EVENT_MOUSEDOWN);
+}
+
+void QStoneOpenGlWidget::mouseMoveEvent(QMouseEvent* qtEvent)
+{
+  mouseEvent(qtEvent, GUIADAPTER_EVENT_MOUSEMOVE);
+}
+
+void QStoneOpenGlWidget::mouseReleaseEvent(QMouseEvent* qtEvent)
+{
+  mouseEvent(qtEvent, GUIADAPTER_EVENT_MOUSEUP);
+}
+
+void ConvertFromPlatform(
+    OrthancStone::GuiAdapterKeyboardEvent& guiEvent,
+    const QKeyEvent& qtEvent)
+{
+  if (qtEvent.text().length() > 0)
+  {
+    guiEvent.sym[0] = qtEvent.text()[0].cell();
+  }
+  else
+  {
+    guiEvent.sym[0] = 0;
+  }
+  guiEvent.sym[1] = 0;
+
+  if (qtEvent.modifiers().testFlag(Qt::ShiftModifier))
+  {
+    guiEvent.shiftKey = true;
+  }
+  if (qtEvent.modifiers().testFlag(Qt::ControlModifier))
+  {
+    guiEvent.ctrlKey = true;
+  }
+  if (qtEvent.modifiers().testFlag(Qt::AltModifier))
+  {
+    guiEvent.altKey = true;
+  }
+
+}
+
+
+bool QStoneOpenGlWidget::keyEvent(QKeyEvent* qtEvent, OrthancStone::GuiAdapterHidEventType guiEventType)
+{
+  bool handled = false;
+  OrthancStone::GuiAdapterKeyboardEvent guiEvent;
+  ConvertFromPlatform(guiEvent, *qtEvent);
+  guiEvent.type = guiEventType;
+
+  if (sceneInteractor_.get() != NULL && compositor_.get() != NULL)
+  {
+    handled = sceneInteractor_->OnKeyboardEvent(guiEvent);
+
+    if (handled)
+    {
+      // force redraw of the OpenGL widget
+      update();
+    }
+  }
+  return handled;
+}
+
+void QStoneOpenGlWidget::keyPressEvent(QKeyEvent *qtEvent)
+{
+  bool handled = keyEvent(qtEvent, GUIADAPTER_EVENT_KEYDOWN);
+  if (!handled)
+  {
+    QOpenGLWidget::keyPressEvent(qtEvent);
+  }
+}
+
+void QStoneOpenGlWidget::keyReleaseEvent(QKeyEvent *qtEvent)
+{
+  bool handled = keyEvent(qtEvent, GUIADAPTER_EVENT_KEYUP);
+  if (!handled)
+  {
+    QOpenGLWidget::keyPressEvent(qtEvent);
+  }
+}
+
+void QStoneOpenGlWidget::wheelEvent(QWheelEvent *qtEvent)
+{
+  OrthancStone::GuiAdapterWheelEvent guiEvent;
+  throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+
+  // force redraw of the OpenGL widget
+  update();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/Deprecated/Qt/QStoneOpenGlWidget.h	Tue Apr 07 14:31:28 2020 +0200
@@ -0,0 +1,90 @@
+#pragma once
+#include "../../Framework/OpenGL/OpenGLIncludes.h"
+#include <QOpenGLWidget>
+#include <QOpenGLFunctions>
+#include <QOpenGLContext>
+
+#include <boost/shared_ptr.hpp>
+#include "../../Framework/OpenGL/IOpenGLContext.h"
+#include "../../Framework/Scene2D/OpenGLCompositor.h"
+#include "../../Framework/Viewport/ViewportBase.h"
+#include "../../Applications/Generic/Scene2DInteractor.h"
+
+namespace OrthancStone
+{
+  class QStoneOpenGlWidget :
+      public QOpenGLWidget,
+      public OpenGL::IOpenGLContext,
+      public ViewportBase
+  {
+    std::unique_ptr<OrthancStone::OpenGLCompositor> compositor_;
+    boost::shared_ptr<Scene2DInteractor> sceneInteractor_;
+    QOpenGLContext                        openGlContext_;
+
+  public:
+    QStoneOpenGlWidget(QWidget *parent) :
+      QOpenGLWidget(parent),
+      ViewportBase("QtStoneOpenGlWidget")  // TODO: we shall be able to define a name but construction time is too early !
+    {
+      setFocusPolicy(Qt::StrongFocus);  // to enable keyPressEvent
+      setMouseTracking(true);           // to enable mouseMoveEvent event when no button is pressed
+    }
+
+    void Init()
+    {
+      QSurfaceFormat requestedFormat;
+      requestedFormat.setVersion( 2, 0 );
+      openGlContext_.setFormat( requestedFormat );
+      openGlContext_.create();
+      openGlContext_.makeCurrent(context()->surface());
+
+      compositor_.reset(new OpenGLCompositor(*this, GetScene()));
+    }
+
+  protected:
+
+    //**** QWidget overrides
+    void initializeGL() override;
+    void resizeGL(int w, int h) override;
+    void paintGL() override;
+
+    void mousePressEvent(QMouseEvent* event) override;
+    void mouseMoveEvent(QMouseEvent* event) override;
+    void mouseReleaseEvent(QMouseEvent* event) override;
+    void keyPressEvent(QKeyEvent* event) override;
+    void keyReleaseEvent(QKeyEvent *event) override;
+    void wheelEvent(QWheelEvent* event) override;
+
+    //**** IOpenGLContext overrides
+
+    virtual void MakeCurrent() override;
+    virtual void SwapBuffer() override {}
+
+    virtual unsigned int GetCanvasWidth() const override
+    {
+      return this->width();
+    }
+
+    virtual unsigned int GetCanvasHeight() const override
+    {
+      return this->height();
+    }
+
+  public:
+
+    void SetInteractor(boost::shared_ptr<Scene2DInteractor> sceneInteractor)
+    {
+      sceneInteractor_ = sceneInteractor;
+    }
+
+    virtual ICompositor& GetCompositor()
+    {
+      return *compositor_;
+    }
+
+  protected:
+    void mouseEvent(QMouseEvent* qtEvent, OrthancStone::GuiAdapterHidEventType guiEventType);
+    bool keyEvent(QKeyEvent* qtEvent, OrthancStone::GuiAdapterHidEventType guiEventType);
+
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/Deprecated/Qt/Scene2DInteractor.cpp	Tue Apr 07 14:31:28 2020 +0200
@@ -0,0 +1,72 @@
+#include "Scene2DInteractor.h"
+
+#include "../../Framework/Scene2D/PanSceneTracker.h"
+#include "../../Framework/Scene2D/ZoomSceneTracker.h"
+#include "../../Framework/Scene2D/RotateSceneTracker.h"
+
+
+namespace OrthancStone
+{
+
+}
+
+using namespace OrthancStone;
+
+
+bool BasicScene2DInteractor::OnMouseEvent(const GuiAdapterMouseEvent& event, const PointerEvent& pointerEvent)
+{
+  if (currentTracker_.get() != NULL)
+  {
+    switch (event.type)
+    {
+    case GUIADAPTER_EVENT_MOUSEUP:
+    {
+      currentTracker_->PointerUp(pointerEvent);
+      if (!currentTracker_->IsAlive())
+      {
+        currentTracker_.reset();
+      }
+    };break;
+    case GUIADAPTER_EVENT_MOUSEMOVE:
+    {
+      currentTracker_->PointerMove(pointerEvent);
+    };break;
+    }
+    return true;
+  }
+  else
+  {
+    if (event.button == GUIADAPTER_MOUSEBUTTON_LEFT)
+    {
+      currentTracker_.reset(new RotateSceneTracker(viewportController_, pointerEvent));
+    }
+    else if (event.button == GUIADAPTER_MOUSEBUTTON_MIDDLE)
+    {
+      currentTracker_.reset(new PanSceneTracker(viewportController_, pointerEvent));
+    }
+    else if (event.button == GUIADAPTER_MOUSEBUTTON_RIGHT && compositor_.get() != NULL)
+    {
+      currentTracker_.reset(new ZoomSceneTracker(viewportController_, pointerEvent, compositor_->GetHeight()));
+    }
+    return true;
+  }
+  return false;
+}
+
+bool BasicScene2DInteractor::OnKeyboardEvent(const GuiAdapterKeyboardEvent& guiEvent)
+{
+  switch (guiEvent.sym[0])
+  {
+  case 's':
+  {
+    viewportController_->FitContent(compositor_->GetWidth(), compositor_->GetHeight());
+    return true;
+  };
+  }
+  return false;
+}
+
+bool BasicScene2DInteractor::OnWheelEvent(const GuiAdapterWheelEvent& guiEvent)
+{
+  return false;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/Deprecated/Qt/Scene2DInteractor.h	Tue Apr 07 14:31:28 2020 +0200
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "../../Applications/Generic/Scene2DInteractor.h"
+#include "../../Framework/Scene2DViewport/IFlexiblePointerTracker.h"
+
+
+class BasicScene2DInteractor : public OrthancStone::Scene2DInteractor
+{
+  boost::shared_ptr<OrthancStone::IFlexiblePointerTracker>  currentTracker_;
+public:
+  BasicScene2DInteractor(boost::shared_ptr<OrthancStone::ViewportController> viewportController) :
+    Scene2DInteractor(viewportController)
+  {}
+
+  virtual bool OnMouseEvent(const OrthancStone::GuiAdapterMouseEvent& event, const OrthancStone::PointerEvent& pointerEvent) override;
+  virtual bool OnKeyboardEvent(const OrthancStone::GuiAdapterKeyboardEvent& guiEvent);
+  virtual bool OnWheelEvent(const OrthancStone::GuiAdapterWheelEvent& guiEvent);
+};
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/Deprecated/Sdl/BasicScene.cpp	Tue Apr 07 14:31:28 2020 +0200
@@ -0,0 +1,418 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 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/>.
+ **/
+
+
+// From Stone
+#include "../../Framework/Viewport/SdlViewport.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/ZoomSceneTracker.h"
+#include "../../Framework/Scene2DViewport/ViewportController.h"
+#include "../../Framework/Scene2DViewport/UndoStack.h"
+
+#include "../../Framework/StoneInitialization.h"
+#include "../../Framework/Messages/MessageBroker.h"
+
+// From Orthanc framework
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+#include <Core/Images/Image.h>
+#include <Core/Images/ImageProcessing.h>
+#include <Core/Images/PngWriter.h>
+
+#include <boost/make_shared.hpp>
+
+#include <SDL.h>
+#include <stdio.h>
+
+static const unsigned int FONT_SIZE = 32;
+static const int LAYER_POSITION = 150;
+
+#define OPENGL_ENABLED 0
+
+void PrepareScene(OrthancStone::Scene2D& scene)
+{
+  using namespace OrthancStone;
+
+  // Texture of 2x2 size
+  {
+    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::unique_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
+  {
+    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::unique_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::unique_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer);
+
+    layer->SetThickness(10);
+
+    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, 255, 0, 0);
+
+    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, 0, 255, 0);
+
+    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, 0, 0, 255);
+
+    scene.SetLayer(50, layer.release());
+  }
+
+  // Some text
+  {
+    std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer);
+    layer->SetText("Hello");
+    scene.SetLayer(100, layer.release());
+  }
+}
+
+
+void TakeScreenshot(const std::string& target,
+                    const OrthancStone::Scene2D& scene,
+                    unsigned int canvasWidth,
+                    unsigned int canvasHeight)
+{
+  using namespace OrthancStone;
+  // Take a screenshot, then save it as PNG file
+  CairoCompositor compositor(scene, canvasWidth, canvasHeight);
+  compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, FONT_SIZE, Orthanc::Encoding_Latin1);
+  compositor.Refresh();
+
+  Orthanc::ImageAccessor canvas;
+  compositor.GetCanvas().GetReadOnlyAccessor(canvas);
+
+  Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false);
+  Orthanc::ImageProcessing::Convert(png, canvas);
+        
+  Orthanc::PngWriter writer;
+  writer.WriteToFile(target, png);
+}
+
+
+void HandleApplicationEvent(const SDL_Event& event,
+                            boost::shared_ptr<OrthancStone::ViewportController>& controller,
+                            boost::shared_ptr<OrthancStone::IFlexiblePointerTracker>& activeTracker)
+{
+  using namespace OrthancStone;
+
+  Scene2D& scene = controller->GetScene();
+  IViewport& viewport = controller->GetViewport();
+
+  if (event.type == SDL_MOUSEMOTION)
+  {
+    int scancodeCount = 0;
+    const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount);
+
+    if (activeTracker.get() == NULL &&
+        SDL_SCANCODE_LCTRL < scancodeCount &&
+        keyboardState[SDL_SCANCODE_LCTRL])
+    {
+      // The "left-ctrl" key is down, while no tracker is present
+
+      PointerEvent e;
+      e.AddPosition(viewport.GetPixelCenterCoordinates(event.button.x, event.button.y));
+
+      ScenePoint2D p = e.GetMainPosition().Apply(scene.GetCanvasToSceneTransform());
+
+      char buf[64];
+      sprintf(buf, "(%0.02f,%0.02f)", p.GetX(), p.GetY());
+
+      if (scene.HasLayer(LAYER_POSITION))
+      {
+        TextSceneLayer& layer =
+          dynamic_cast<TextSceneLayer&>(scene.GetLayer(LAYER_POSITION));
+        layer.SetText(buf);
+        layer.SetPosition(p.GetX(), p.GetY());
+      }
+      else
+      {
+        std::unique_ptr<TextSceneLayer> 
+          layer(new TextSceneLayer);
+        layer->SetColor(0, 255, 0);
+        layer->SetText(buf);
+        layer->SetBorder(20);
+        layer->SetAnchor(BitmapAnchor_BottomCenter);
+        layer->SetPosition(p.GetX(), p.GetY());
+        scene.SetLayer(LAYER_POSITION, layer.release());
+      }
+    }
+    else
+    {
+      scene.DeleteLayer(LAYER_POSITION);
+    }
+  }
+  else if (event.type == SDL_MOUSEBUTTONDOWN)
+  {
+    PointerEvent e;
+    e.AddPosition(viewport.GetPixelCenterCoordinates(event.button.x, event.button.y));
+
+    switch (event.button.button)
+    {
+      case SDL_BUTTON_MIDDLE:
+        activeTracker = boost::make_shared<PanSceneTracker>(controller, e);
+        break;
+
+      case SDL_BUTTON_RIGHT:
+        activeTracker = boost::make_shared<ZoomSceneTracker>
+          (controller, e, viewport.GetCanvasHeight());
+        break;
+
+      case SDL_BUTTON_LEFT:
+        activeTracker = boost::make_shared<RotateSceneTracker>(controller, e);
+        break;
+
+      default:
+        break;
+    }
+  }
+  else if (event.type == SDL_KEYDOWN &&
+           event.key.repeat == 0 /* Ignore key bounce */)
+  {
+    switch (event.key.keysym.sym)
+    {
+      case SDLK_s:
+        controller->FitContent(viewport.GetCanvasWidth(), 
+                               viewport.GetCanvasHeight());
+        break;
+              
+      case SDLK_c:
+        TakeScreenshot("screenshot.png", scene, 
+                       viewport.GetCanvasWidth(), 
+                       viewport.GetCanvasHeight());
+        break;
+              
+      default:
+        break;
+    }
+  }
+}
+
+#if OPENGL_ENABLED==1
+static void GLAPIENTRY
+OpenGLMessageCallback(GLenum source,
+                      GLenum type,
+                      GLuint id,
+                      GLenum severity,
+                      GLsizei length,
+                      const GLchar* message,
+                      const void* userParam )
+{
+  if (severity != GL_DEBUG_SEVERITY_NOTIFICATION)
+  {
+    fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
+            ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ),
+            type, severity, message );
+  }
+}
+#endif
+
+void Run(OrthancStone::MessageBroker& broker,
+         OrthancStone::SdlViewport& viewport)
+{
+  using namespace OrthancStone;
+  
+  boost::shared_ptr<ViewportController> controller(
+    new ViewportController(boost::make_shared<UndoStack>(), broker, viewport));
+  
+#if OPENGL_ENABLED==1
+  glEnable(GL_DEBUG_OUTPUT);
+  glDebugMessageCallback(OpenGLMessageCallback, 0);
+#endif
+
+  boost::shared_ptr<IFlexiblePointerTracker> tracker;
+
+  bool firstShown = true;
+  bool stop = false;
+  while (!stop)
+  {
+    viewport.Refresh();
+
+    SDL_Event event;
+    while (!stop &&
+           SDL_PollEvent(&event))
+    {
+      if (event.type == SDL_QUIT)
+      {
+        stop = true;
+        break;
+      }
+      else if (event.type == SDL_MOUSEMOTION)
+      {
+        if (tracker)
+        {
+          PointerEvent e;
+          e.AddPosition(viewport.GetPixelCenterCoordinates(
+            event.button.x, event.button.y));
+          tracker->PointerMove(e);
+        }
+      }
+      else if (event.type == SDL_MOUSEBUTTONUP)
+      {
+        if (tracker)
+        {
+          PointerEvent e;
+          e.AddPosition(viewport.GetPixelCenterCoordinates(
+            event.button.x, event.button.y));
+          tracker->PointerUp(e);
+          if(!tracker->IsAlive())
+            tracker.reset();
+        }
+      }
+      else if (event.type == SDL_WINDOWEVENT)
+      {
+        switch (event.window.event)
+        {
+          case SDL_WINDOWEVENT_SIZE_CHANGED:
+            tracker.reset();
+            viewport.UpdateSize(event.window.data1, event.window.data2);
+            break;
+
+          case SDL_WINDOWEVENT_SHOWN:
+            if (firstShown)
+            {
+              // Once the window is first shown, fit the content to its size
+              controller->FitContent(viewport.GetCanvasWidth(), viewport.GetCanvasHeight());
+              firstShown = false;
+            }
+            
+            break;
+
+          default:
+            break;
+        }
+      }
+      else if (event.type == SDL_KEYDOWN &&
+               event.key.repeat == 0 /* Ignore key bounce */)
+      {
+        switch (event.key.keysym.sym)
+        {
+          case SDLK_f:
+            viewport.GetWindow().ToggleMaximize();
+            break;
+              
+          case SDLK_q:
+            stop = true;
+            break;
+
+          default:
+            break;
+        }
+      }
+      
+      HandleApplicationEvent(event, controller, tracker);
+    }
+
+    SDL_Delay(1);
+  }
+}
+
+
+
+
+/**
+ * IMPORTANT: The full arguments to "main()" are needed for SDL on
+ * Windows. Otherwise, one gets the linking error "undefined reference
+ * to `SDL_main'". https://wiki.libsdl.org/FAQWindows
+ **/
+int main(int argc, char* argv[])
+{
+  OrthancStone::StoneInitialize();
+  Orthanc::Logging::EnableInfoLevel(true);
+
+  try
+  {
+#if OPENGL_ENABLED==1
+    OrthancStone::SdlOpenGLViewport viewport("Hello", 1024, 768);
+#else
+    OrthancStone::SdlCairoViewport viewport("Hello", 1024, 768);
+#endif
+    PrepareScene(viewport.GetScene());
+
+    viewport.GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, 
+                                     FONT_SIZE, Orthanc::Encoding_Latin1);
+    
+    OrthancStone::MessageBroker broker;
+    Run(broker, viewport);
+  }
+  catch (Orthanc::OrthancException& e)
+  {
+    LOG(ERROR) << "EXCEPTION: " << e.What();
+  }
+
+  OrthancStone::StoneFinalize();
+
+  return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/Deprecated/Sdl/CMakeLists.txt	Tue Apr 07 14:31:28 2020 +0200
@@ -0,0 +1,129 @@
+cmake_minimum_required(VERSION 2.8.3)
+
+#####################################################################
+## 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_SDL_CONSOLE OFF CACHE BOOL "Enable the use of the MIT-licensed SDL_Console")
+SET(ENABLE_GOOGLE_TEST OFF)
+SET(ENABLE_LOCALE ON)
+SET(ENABLE_SDL ON)
+SET(ENABLE_WEB_CLIENT ON)
+SET(ORTHANC_SANDBOXED OFF)
+LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options)
+
+include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneConfiguration.cmake)
+
+add_definitions(
+  -DORTHANC_ENABLE_LOGGING_PLUGIN=0
+  )
+
+
+#####################################################################
+## Build the samples
+#####################################################################
+
+add_library(OrthancStone STATIC
+  ${ORTHANC_STONE_SOURCES}
+  )
+
+#
+# BasicScene
+# 
+
+add_executable(BasicScene
+  BasicScene.cpp
+  )
+
+target_link_libraries(BasicScene OrthancStone)
+
+#
+# TrackerSample
+# 
+
+LIST(APPEND TRACKERSAMPLE_SOURCE "TrackerSample.cpp")
+LIST(APPEND TRACKERSAMPLE_SOURCE "TrackerSampleApp.cpp")
+LIST(APPEND TRACKERSAMPLE_SOURCE "TrackerSampleApp.h")
+
+if (MSVC AND MSVC_VERSION GREATER 1700)
+  LIST(APPEND TRACKERSAMPLE_SOURCE "cpp.hint")
+endif()
+
+add_executable(TrackerSample
+  ${TRACKERSAMPLE_SOURCE}
+  )
+
+target_link_libraries(TrackerSample OrthancStone)
+
+#
+# Loader
+# 
+
+add_executable(Loader
+  Loader.cpp
+  )
+
+target_link_libraries(Loader OrthancStone)
+
+#
+# FusionMprSdl
+# 
+
+add_executable(FusionMprSdl
+  FusionMprSdl.cpp
+  FusionMprSdl.h
+)
+
+target_link_libraries(FusionMprSdl OrthancStone)
+
+#
+# Multiplatform Basic Scene
+#
+
+LIST(APPEND MP_BASIC_SCENE_SOURCE "../MultiPlatform/BasicScene/BasicScene.cpp")
+LIST(APPEND MP_BASIC_SCENE_SOURCE "../MultiPlatform/BasicScene/BasicScene.h")
+LIST(APPEND MP_BASIC_SCENE_SOURCE "../MultiPlatform/BasicScene/mainSdl.cpp")
+
+if (MSVC AND MSVC_VERSION GREATER 1700)
+  LIST(APPEND MP_BASIC_SCENE_SOURCE "cpp.hint")
+endif()
+
+add_executable(MpBasicScene
+  ${MP_BASIC_SCENE_SOURCE}
+  )
+
+target_include_directories(MpBasicScene PUBLIC ${ORTHANC_STONE_ROOT})
+target_link_libraries(MpBasicScene OrthancStone)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/Deprecated/Sdl/FusionMprSdl.cpp	Tue Apr 07 14:31:28 2020 +0200
@@ -0,0 +1,805 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 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 "FusionMprSdl.h"
+
+#include "../../Framework/OpenGL/SdlOpenGLContext.h"
+
+#include "../../Framework/StoneInitialization.h"
+
+#include "../../Framework/Scene2D/CairoCompositor.h"
+#include "../../Framework/Scene2D/ColorTextureSceneLayer.h"
+#include "../../Framework/Scene2D/OpenGLCompositor.h"
+#include "../../Framework/Scene2D/PanSceneTracker.h"
+#include "../../Framework/Scene2D/ZoomSceneTracker.h"
+#include "../../Framework/Scene2D/RotateSceneTracker.h"
+
+#include "../../Framework/Scene2DViewport/UndoStack.h"
+#include "../../Framework/Scene2DViewport/CreateLineMeasureTracker.h"
+#include "../../Framework/Scene2DViewport/CreateAngleMeasureTracker.h"
+#include "../../Framework/Scene2DViewport/IFlexiblePointerTracker.h"
+#include "../../Framework/Scene2DViewport/MeasureTool.h"
+#include "../../Framework/Scene2DViewport/PredeclaredTypes.h"
+
+#include "../../Framework/Volumes/VolumeSceneLayerSource.h"
+
+#include <Core/Images/Image.h>
+#include <Core/Images/ImageProcessing.h>
+#include <Core/Images/PngWriter.h>
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+#include <boost/make_shared.hpp>
+
+#include <stdio.h>
+#include "../../Framework/Oracle/GetOrthancWebViewerJpegCommand.h"
+#include "../../Framework/Oracle/ThreadedOracle.h"
+#include "../../Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h"
+#include "../../Framework/Loaders/OrthancMultiframeVolumeLoader.h"
+#include "../../Framework/Loaders/DicomStructureSetLoader.h"
+#include "../../Framework/Scene2D/GrayscaleStyleConfigurator.h"
+#include "../../Framework/Scene2D/LookupTableStyleConfigurator.h"
+#include "../../Framework/Volumes/DicomVolumeImageMPRSlicer.h"
+#include "Core/SystemToolbox.h"
+
+namespace OrthancStone
+{
+  const char* FusionMprMeasureToolToString(size_t i)
+  {
+    static const char* descs[] = {
+      "FusionMprGuiTool_Rotate",
+      "FusionMprGuiTool_Pan",
+      "FusionMprGuiTool_Zoom",
+      "FusionMprGuiTool_LineMeasure",
+      "FusionMprGuiTool_CircleMeasure",
+      "FusionMprGuiTool_AngleMeasure",
+      "FusionMprGuiTool_EllipseMeasure",
+      "FusionMprGuiTool_LAST"
+    };
+    if (i >= FusionMprGuiTool_LAST)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Wrong tool index");
+    }
+    return descs[i];
+  }
+
+  Scene2D& FusionMprSdlApp::GetScene()
+  {
+    return controller_->GetScene();
+  }
+
+  const Scene2D& FusionMprSdlApp::GetScene() const
+  {
+    return controller_->GetScene();
+  }
+
+  void FusionMprSdlApp::SelectNextTool()
+  {
+    currentTool_ = static_cast<FusionMprGuiTool>(currentTool_ + 1);
+    if (currentTool_ == FusionMprGuiTool_LAST)
+      currentTool_ = static_cast<FusionMprGuiTool>(0);;
+    printf("Current tool is now: %s\n", FusionMprMeasureToolToString(currentTool_));
+  }
+
+  void FusionMprSdlApp::DisplayInfoText()
+  {
+    // do not try to use stuff too early!
+    ICompositor* pCompositor = &(viewport_.GetCompositor());
+    if (pCompositor == NULL)
+      return;
+
+    std::stringstream msg;
+	
+	for (std::map<std::string, std::string>::const_iterator kv = infoTextMap_.begin();
+		kv != infoTextMap_.end(); ++kv)
+    {
+      msg << kv->first << " : " << kv->second << std::endl;
+    }
+	std::string msgS = msg.str();
+
+    TextSceneLayer* layerP = NULL;
+    if (GetScene().HasLayer(FIXED_INFOTEXT_LAYER_ZINDEX))
+    {
+      TextSceneLayer& layer = dynamic_cast<TextSceneLayer&>(
+        GetScene().GetLayer(FIXED_INFOTEXT_LAYER_ZINDEX));
+      layerP = &layer;
+    }
+    else
+    {
+      std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer);
+      layerP = layer.get();
+      layer->SetColor(0, 255, 0);
+      layer->SetFontIndex(1);
+      layer->SetBorder(20);
+      layer->SetAnchor(BitmapAnchor_TopLeft);
+      //layer->SetPosition(0,0);
+      GetScene().SetLayer(FIXED_INFOTEXT_LAYER_ZINDEX, layer.release());
+    }
+    // position the fixed info text in the upper right corner
+    layerP->SetText(msgS.c_str());
+    double cX = viewport_.GetCompositor().GetCanvasWidth() * (-0.5);
+    double cY = viewport_.GetCompositor().GetCanvasHeight() * (-0.5);
+    GetScene().GetCanvasToSceneTransform().Apply(cX,cY);
+    layerP->SetPosition(cX, cY);
+  }
+
+  void FusionMprSdlApp::DisplayFloatingCtrlInfoText(const PointerEvent& e)
+  {
+    ScenePoint2D p = e.GetMainPosition().Apply(GetScene().GetCanvasToSceneTransform());
+
+    char buf[128];
+    sprintf(buf, "S:(%0.02f,%0.02f) C:(%0.02f,%0.02f)", 
+      p.GetX(), p.GetY(), 
+      e.GetMainPosition().GetX(), e.GetMainPosition().GetY());
+
+    if (GetScene().HasLayer(FLOATING_INFOTEXT_LAYER_ZINDEX))
+    {
+      TextSceneLayer& layer =
+        dynamic_cast<TextSceneLayer&>(GetScene().GetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX));
+      layer.SetText(buf);
+      layer.SetPosition(p.GetX(), p.GetY());
+    }
+    else
+    {
+      std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer);
+      layer->SetColor(0, 255, 0);
+      layer->SetText(buf);
+      layer->SetBorder(20);
+      layer->SetAnchor(BitmapAnchor_BottomCenter);
+      layer->SetPosition(p.GetX(), p.GetY());
+      GetScene().SetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX, layer.release());
+    }
+  }
+
+  void FusionMprSdlApp::HideInfoText()
+  {
+    GetScene().DeleteLayer(FLOATING_INFOTEXT_LAYER_ZINDEX);
+  }
+
+  void FusionMprSdlApp::HandleApplicationEvent(
+    const SDL_Event & event)
+  {
+    DisplayInfoText();
+
+    if (event.type == SDL_MOUSEMOTION)
+    {
+      int scancodeCount = 0;
+      const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount);
+
+      if (activeTracker_.get() == NULL &&
+        SDL_SCANCODE_LALT < scancodeCount &&
+        keyboardState[SDL_SCANCODE_LALT])
+      {
+        // The "left-ctrl" key is down, while no tracker is present
+        // Let's display the info text
+        PointerEvent e;
+        e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(
+          event.button.x, event.button.y));
+
+        DisplayFloatingCtrlInfoText(e);
+      }
+      else
+      {
+        HideInfoText();
+        //LOG(TRACE) << "(event.type == SDL_MOUSEMOTION)";
+        if (activeTracker_.get() != NULL)
+        {
+          //LOG(TRACE) << "(activeTracker_.get() != NULL)";
+          PointerEvent e;
+          e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(
+            event.button.x, event.button.y));
+          
+          //LOG(TRACE) << "event.button.x = " << event.button.x << "     " <<
+          //  "event.button.y = " << event.button.y;
+          LOG(TRACE) << "activeTracker_->PointerMove(e); " <<
+            e.GetMainPosition().GetX() << " " << e.GetMainPosition().GetY();
+          
+          activeTracker_->PointerMove(e);
+          if (!activeTracker_->IsAlive())
+            activeTracker_.reset();
+        }
+      }
+    }
+    else if (event.type == SDL_MOUSEBUTTONUP)
+    {
+      if (activeTracker_)
+      {
+        PointerEvent e;
+        e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(event.button.x, event.button.y));
+        activeTracker_->PointerUp(e);
+        if (!activeTracker_->IsAlive())
+          activeTracker_.reset();
+      }
+    }
+    else if (event.type == SDL_MOUSEBUTTONDOWN)
+    {
+      PointerEvent e;
+      e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(
+        event.button.x, event.button.y));
+      if (activeTracker_)
+      {
+        activeTracker_->PointerDown(e);
+        if (!activeTracker_->IsAlive())
+          activeTracker_.reset();
+      }
+      else
+      {
+        // we ATTEMPT to create a tracker if need be
+        activeTracker_ = CreateSuitableTracker(event, e);
+      }
+    }
+    else if (event.type == SDL_KEYDOWN &&
+      event.key.repeat == 0 /* Ignore key bounce */)
+    {
+      switch (event.key.keysym.sym)
+      {
+      case SDLK_ESCAPE:
+        if (activeTracker_)
+        {
+          activeTracker_->Cancel();
+          if (!activeTracker_->IsAlive())
+            activeTracker_.reset();
+        }
+        break;
+
+      case SDLK_t:
+        if (!activeTracker_)
+          SelectNextTool();
+        else
+        {
+          LOG(WARNING) << "You cannot change the active tool when an interaction"
+            " is taking place";
+        }
+        break;
+      case SDLK_s:
+        controller_->FitContent(viewport_.GetCompositor().GetCanvasWidth(),
+          viewport_.GetCompositor().GetCanvasHeight());
+        break;
+
+      case SDLK_z:
+        LOG(TRACE) << "SDLK_z has been pressed. event.key.keysym.mod == " << event.key.keysym.mod;
+        if (event.key.keysym.mod & KMOD_CTRL)
+        {
+          if (controller_->CanUndo())
+          {
+            LOG(TRACE) << "Undoing...";
+            controller_->Undo();
+          }
+          else
+          {
+            LOG(WARNING) << "Nothing to undo!!!";
+          }
+        }
+        break;
+
+      case SDLK_y:
+        LOG(TRACE) << "SDLK_y has been pressed. event.key.keysym.mod == " << event.key.keysym.mod;
+        if (event.key.keysym.mod & KMOD_CTRL)
+        {
+          if (controller_->CanRedo())
+          {
+            LOG(TRACE) << "Redoing...";
+            controller_->Redo();
+          }
+          else
+          {
+            LOG(WARNING) << "Nothing to redo!!!";
+          }
+        }
+        break;
+
+      case SDLK_c:
+        TakeScreenshot(
+          "screenshot.png",
+          viewport_.GetCompositor().GetCanvasWidth(),
+          viewport_.GetCompositor().GetCanvasHeight());
+        break;
+
+      default:
+        break;
+      }
+    }
+  }
+
+
+  void FusionMprSdlApp::OnSceneTransformChanged(
+    const ViewportController::SceneTransformChanged& message)
+  {
+    DisplayInfoText();
+  }
+
+  boost::shared_ptr<IFlexiblePointerTracker> FusionMprSdlApp::CreateSuitableTracker(
+    const SDL_Event & event,
+    const PointerEvent & e)
+  {
+    using namespace Orthanc;
+
+    switch (event.button.button)
+    {
+    case SDL_BUTTON_MIDDLE:
+      return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker
+        (controller_, e));
+
+    case SDL_BUTTON_RIGHT:
+      return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker
+        (controller_, e, viewport_.GetCompositor().GetCanvasHeight()));
+
+    case SDL_BUTTON_LEFT:
+    {
+      //LOG(TRACE) << "CreateSuitableTracker: case SDL_BUTTON_LEFT:";
+      // TODO: we need to iterate on the set of measuring tool and perform
+      // a hit test to check if a tracker needs to be created for edition.
+      // Otherwise, depending upon the active tool, we might want to create
+      // a "measuring tool creation" tracker
+
+      // TODO: if there are conflicts, we should prefer a tracker that 
+      // pertains to the type of measuring tool currently selected (TBD?)
+      boost::shared_ptr<IFlexiblePointerTracker> hitTestTracker = TrackerHitTest(e);
+
+      if (hitTestTracker != NULL)
+      {
+        //LOG(TRACE) << "hitTestTracker != NULL";
+        return hitTestTracker;
+      }
+      else
+      {
+        switch (currentTool_)
+        {
+        case FusionMprGuiTool_Rotate:
+          //LOG(TRACE) << "Creating RotateSceneTracker";
+          return boost::shared_ptr<IFlexiblePointerTracker>(new RotateSceneTracker(
+            controller_, e));
+        case FusionMprGuiTool_Pan:
+          return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker(
+            controller_, e));
+        case FusionMprGuiTool_Zoom:
+          return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker(
+            controller_, e, viewport_.GetCompositor().GetCanvasHeight()));
+        //case GuiTool_AngleMeasure:
+        //  return new AngleMeasureTracker(GetScene(), e);
+        //case GuiTool_CircleMeasure:
+        //  return new CircleMeasureTracker(GetScene(), e);
+        //case GuiTool_EllipseMeasure:
+        //  return new EllipseMeasureTracker(GetScene(), e);
+        case FusionMprGuiTool_LineMeasure:
+          return boost::shared_ptr<IFlexiblePointerTracker>(new CreateLineMeasureTracker(
+            IObserver::GetBroker(), controller_, e));
+        case FusionMprGuiTool_AngleMeasure:
+          return boost::shared_ptr<IFlexiblePointerTracker>(new CreateAngleMeasureTracker(
+            IObserver::GetBroker(), controller_, e));
+        case FusionMprGuiTool_CircleMeasure:
+          LOG(ERROR) << "Not implemented yet!";
+          return boost::shared_ptr<IFlexiblePointerTracker>();
+        case FusionMprGuiTool_EllipseMeasure:
+          LOG(ERROR) << "Not implemented yet!";
+          return boost::shared_ptr<IFlexiblePointerTracker>();
+        default:
+          throw OrthancException(ErrorCode_InternalError, "Wrong tool!");
+        }
+      }
+    }
+    default:
+      return boost::shared_ptr<IFlexiblePointerTracker>();
+    }
+  }
+
+
+  FusionMprSdlApp::FusionMprSdlApp(MessageBroker& broker)
+    : IObserver(broker)
+    , broker_(broker)
+    , oracleObservable_(broker)
+    , oracle_(*this)
+    , currentTool_(FusionMprGuiTool_Rotate)
+    , undoStack_(new UndoStack)
+    , viewport_("Hello", 1024, 1024, false) // False means we do NOT let Windows treat this as a legacy application that needs to be scaled
+  {
+    //oracleObservable.RegisterObserverCallback
+    //(new Callable
+    //  <FusionMprSdlApp, SleepOracleCommand::TimeoutMessage>(*this, &FusionMprSdlApp::Handle));
+
+    //oracleObservable.RegisterObserverCallback
+    //(new Callable
+    //  <Toto, GetOrthancImageCommand::SuccessMessage>(*this, &FusionMprSdlApp::Handle));
+
+    //oracleObservable.RegisterObserverCallback
+    //(new Callable
+    //  <FusionMprSdlApp, GetOrthancWebViewerJpegCommand::SuccessMessage>(*this, &ToFusionMprSdlAppto::Handle));
+
+    oracleObservable_.RegisterObserverCallback
+    (new Callable
+      <FusionMprSdlApp, OracleCommandExceptionMessage>(*this, &FusionMprSdlApp::Handle));
+    
+    controller_ = boost::shared_ptr<ViewportController>(
+      new ViewportController(undoStack_, broker_, viewport_));
+
+    controller_->RegisterObserverCallback(
+      new Callable<FusionMprSdlApp, ViewportController::SceneTransformChanged>
+      (*this, &FusionMprSdlApp::OnSceneTransformChanged));
+
+    TEXTURE_2x2_1_ZINDEX = 1;
+    TEXTURE_1x1_ZINDEX = 2;
+    TEXTURE_2x2_2_ZINDEX = 3;
+    LINESET_1_ZINDEX = 4;
+    LINESET_2_ZINDEX = 5;
+    FLOATING_INFOTEXT_LAYER_ZINDEX = 6;
+    FIXED_INFOTEXT_LAYER_ZINDEX = 7;
+  }
+
+  void FusionMprSdlApp::PrepareScene()
+  {
+    // Texture of 2x2 size
+    {
+      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;
+
+      GetScene().SetLayer(TEXTURE_2x2_1_ZINDEX, new ColorTextureSceneLayer(i));
+    }
+  }
+
+  void FusionMprSdlApp::DisableTracker()
+  {
+    if (activeTracker_)
+    {
+      activeTracker_->Cancel();
+      activeTracker_.reset();
+    }
+  }
+
+  void FusionMprSdlApp::TakeScreenshot(const std::string& target,
+    unsigned int canvasWidth,
+    unsigned int canvasHeight)
+  {
+    CairoCompositor compositor(GetScene(), canvasWidth, canvasHeight);
+    compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, FONT_SIZE_0, Orthanc::Encoding_Latin1);
+    compositor.Refresh();
+
+    Orthanc::ImageAccessor canvas;
+    compositor.GetCanvas().GetReadOnlyAccessor(canvas);
+
+    Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false);
+    Orthanc::ImageProcessing::Convert(png, canvas);
+
+    Orthanc::PngWriter writer;
+    writer.WriteToFile(target, png);
+  }
+
+
+  boost::shared_ptr<IFlexiblePointerTracker> FusionMprSdlApp::TrackerHitTest(const PointerEvent & e)
+  {
+    // std::vector<boost::shared_ptr<MeasureTool>> measureTools_;
+    return boost::shared_ptr<IFlexiblePointerTracker>();
+  }
+
+  static void GLAPIENTRY
+    OpenGLMessageCallback(GLenum source,
+      GLenum type,
+      GLuint id,
+      GLenum severity,
+      GLsizei length,
+      const GLchar* message,
+      const void* userParam)
+  {
+    if (severity != GL_DEBUG_SEVERITY_NOTIFICATION)
+    {
+      fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
+        (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""),
+        type, severity, message);
+    }
+  }
+
+  static bool g_stopApplication = false;
+  
+
+  void FusionMprSdlApp::Handle(const DicomVolumeImage::GeometryReadyMessage& message)
+  {
+    printf("Geometry ready\n");
+
+    //plane_ = message.GetOrigin().GetGeometry().GetSagittalGeometry();
+    //plane_ = message.GetOrigin().GetGeometry().GetAxialGeometry();
+    plane_ = message.GetOrigin().GetGeometry().GetCoronalGeometry();
+    plane_.SetOrigin(message.GetOrigin().GetGeometry().GetCoordinates(0.5f, 0.5f, 0.5f));
+
+    //Refresh();
+  }
+
+
+  void FusionMprSdlApp::Handle(const OracleCommandExceptionMessage& message)
+  {
+    printf("EXCEPTION: [%s] on command type %d\n", message.GetException().What(), message.GetCommand().GetType());
+
+    switch (message.GetCommand().GetType())
+    {
+    case IOracleCommand::Type_GetOrthancWebViewerJpeg:
+      printf("URI: [%s]\n", dynamic_cast<const GetOrthancWebViewerJpegCommand&>
+        (message.GetCommand()).GetUri().c_str());
+      break;
+
+    default:
+      break;
+    }
+  }
+
+  void FusionMprSdlApp::SetVolume1(int depth,
+    const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume,
+    OrthancStone::ILayerStyleConfigurator* style)
+  {
+    source1_.reset(new OrthancStone::VolumeSceneLayerSource(controller_->GetScene(), depth, volume));
+
+    if (style != NULL)
+    {
+      source1_->SetConfigurator(style);
+    }
+  }
+
+  void FusionMprSdlApp::SetVolume2(int depth,
+    const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume,
+    OrthancStone::ILayerStyleConfigurator* style)
+  {
+    source2_.reset(new OrthancStone::VolumeSceneLayerSource(controller_->GetScene(), depth, volume));
+
+    if (style != NULL)
+    {
+      source2_->SetConfigurator(style);
+    }
+  }
+
+  void FusionMprSdlApp::SetStructureSet(int depth,
+    const boost::shared_ptr<OrthancStone::DicomStructureSetLoader>& volume)
+  {
+    source3_.reset(new OrthancStone::VolumeSceneLayerSource(controller_->GetScene(), depth, volume));
+  }
+  
+  void FusionMprSdlApp::Run()
+  {
+    // False means we do NOT let Windows treat this as a legacy application
+    // that needs to be scaled
+    controller_->FitContent(viewport_.GetCanvasWidth(), viewport_.GetCanvasHeight());
+
+    glEnable(GL_DEBUG_OUTPUT);
+    glDebugMessageCallback(OpenGLMessageCallback, 0);
+
+    viewport_.GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT,
+      FONT_SIZE_0, Orthanc::Encoding_Latin1);
+    viewport_.GetCompositor().SetFont(1, Orthanc::EmbeddedResources::UBUNTU_FONT,
+      FONT_SIZE_1, Orthanc::Encoding_Latin1);
+
+
+    //////// from loader
+    {
+      Orthanc::WebServiceParameters p;
+      //p.SetUrl("http://localhost:8043/");
+      p.SetCredentials("orthanc", "orthanc");
+      oracle_.SetOrthancParameters(p);
+    }
+
+    //////// from Run
+
+    boost::shared_ptr<DicomVolumeImage>  ct(new DicomVolumeImage);
+    boost::shared_ptr<DicomVolumeImage>  dose(new DicomVolumeImage);
+
+
+    boost::shared_ptr<OrthancSeriesVolumeProgressiveLoader> ctLoader;
+    boost::shared_ptr<OrthancMultiframeVolumeLoader> doseLoader;
+    boost::shared_ptr<DicomStructureSetLoader>  rtstructLoader;
+
+    {
+      ctLoader.reset(new OrthancSeriesVolumeProgressiveLoader(ct, oracle_, oracleObservable_));
+      doseLoader.reset(new OrthancMultiframeVolumeLoader(dose, oracle_, oracleObservable_));
+      rtstructLoader.reset(new DicomStructureSetLoader(oracle_, oracleObservable_));
+    }
+
+    //toto->SetReferenceLoader(*ctLoader);
+    //doseLoader->RegisterObserverCallback
+    //(new Callable
+    //  <FusionMprSdlApp, DicomVolumeImage::GeometryReadyMessage>(*this, &FusionMprSdlApp::Handle));
+    ctLoader->RegisterObserverCallback
+    (new Callable
+      <FusionMprSdlApp, DicomVolumeImage::GeometryReadyMessage>(*this, &FusionMprSdlApp::Handle));
+
+    this->SetVolume1(0, ctLoader, new GrayscaleStyleConfigurator);
+
+    {
+      std::unique_ptr<LookupTableStyleConfigurator> config(new LookupTableStyleConfigurator);
+      config->SetLookupTable(Orthanc::EmbeddedResources::COLORMAP_HOT);
+
+      boost::shared_ptr<DicomVolumeImageMPRSlicer> tmp(new DicomVolumeImageMPRSlicer(dose));
+      this->SetVolume2(1, tmp, config.release());
+    }
+
+    this->SetStructureSet(2, rtstructLoader);
+
+#if 1
+    /*
+    BGO data
+    http://localhost:8042/twiga-orthanc-viewer-demo/twiga-orthanc-viewer-demo.html?ct-series=a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa
+    &
+    dose-instance=830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb
+    &
+    struct-instance=54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9
+    */
+    ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa");  // CT
+    doseLoader->LoadInstance("830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb");  // RT-DOSE
+    rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9");  // RT-STRUCT
+#else
+    //ctLoader->LoadSeries("cb3ea4d1-d08f3856-ad7b6314-74d88d77-60b05618");  // CT
+    //doseLoader->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6");  // RT-DOSE
+    //rtstructLoader->LoadInstance("83d9c0c3-913a7fee-610097d7-cbf0522d-fd75bee6");  // RT-STRUCT
+
+    // 2017-05-16
+    ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa");  // CT
+    doseLoader->LoadInstance("eac822ef-a395f94e-e8121fe0-8411fef8-1f7bffad");  // RT-DOSE
+    rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9");  // RT-STRUCT
+#endif
+
+    oracle_.Start();
+
+//// END from loader
+
+    while (!g_stopApplication)
+    {
+      viewport_.GetCompositor().Refresh();
+
+//////// from loader
+      if (source1_.get() != NULL)
+      {
+        source1_->Update(plane_);
+      }
+
+      if (source2_.get() != NULL)
+      {
+        source2_->Update(plane_);
+      }
+
+      if (source3_.get() != NULL)
+      {
+        source3_->Update(plane_);
+      }
+//// END from loader
+
+      SDL_Event event;
+      while (!g_stopApplication && SDL_PollEvent(&event))
+      {
+        if (event.type == SDL_QUIT)
+        {
+          g_stopApplication = true;
+          break;
+        }
+        else if (event.type == SDL_WINDOWEVENT &&
+          event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
+        {
+          DisableTracker(); // was: tracker.reset(NULL);
+        }
+        else if (event.type == SDL_KEYDOWN &&
+          event.key.repeat == 0 /* Ignore key bounce */)
+        {
+          switch (event.key.keysym.sym)
+          {
+          case SDLK_f:
+            viewport_.GetWindow().ToggleMaximize();
+            break;
+
+          case SDLK_s:
+            controller_->FitContent(viewport_.GetCanvasWidth(), viewport_.GetCanvasHeight());
+            break;
+
+          case SDLK_q:
+            g_stopApplication = true;
+            break;
+          default:
+            break;
+          }
+        }
+        HandleApplicationEvent(event);
+      }
+      SDL_Delay(1);
+    }
+
+    //// from loader
+
+    //Orthanc::SystemToolbox::ServerBarrier();
+
+    /**
+     * WARNING => The oracle must be stopped BEFORE the objects using
+     * it are destroyed!!! This forces to wait for the completion of
+     * the running callback methods. Otherwise, the callbacks methods
+     * might still be running while their parent object is destroyed,
+     * resulting in crashes. This is very visible if adding a sleep(),
+     * as in (*).
+     **/
+
+    oracle_.Stop();
+    //// END from loader
+  }
+
+  void FusionMprSdlApp::SetInfoDisplayMessage(
+    std::string key, std::string value)
+  {
+    if (value == "")
+      infoTextMap_.erase(key);
+    else
+      infoTextMap_[key] = value;
+    DisplayInfoText();
+  }
+
+}
+
+
+boost::weak_ptr<OrthancStone::FusionMprSdlApp> g_app;
+
+void FusionMprSdl_SetInfoDisplayMessage(std::string key, std::string value)
+{
+  boost::shared_ptr<OrthancStone::FusionMprSdlApp> app = g_app.lock();
+  if (app)
+  {
+    app->SetInfoDisplayMessage(key, value);
+  }
+}
+
+/**
+ * IMPORTANT: The full arguments to "main()" are needed for SDL on
+ * Windows. Otherwise, one gets the linking error "undefined reference
+ * to `SDL_main'". https://wiki.libsdl.org/FAQWindows
+ **/
+int main(int argc, char* argv[])
+{
+  using namespace OrthancStone;
+
+  StoneInitialize();
+  Orthanc::Logging::EnableInfoLevel(true);
+//  Orthanc::Logging::EnableTraceLevel(true);
+
+  try
+  {
+    OrthancStone::MessageBroker broker;
+    boost::shared_ptr<FusionMprSdlApp> app(new FusionMprSdlApp(broker));
+    g_app = app;
+    app->PrepareScene();
+    app->Run();
+  }
+  catch (Orthanc::OrthancException& e)
+  {
+    LOG(ERROR) << "EXCEPTION: " << e.What();
+  }
+
+  StoneFinalize();
+
+  return 0;
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/Deprecated/Sdl/FusionMprSdl.h	Tue Apr 07 14:31:28 2020 +0200
@@ -0,0 +1,206 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 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 "../../Framework/Viewport/SdlViewport.h"
+
+#include "../../Framework/Messages/IObserver.h"
+#include "../../Framework/Messages/IMessageEmitter.h"
+#include "../../Framework/Oracle/OracleCommandExceptionMessage.h"
+#include "../../Framework/Scene2DViewport/ViewportController.h"
+#include "../../Framework/Volumes/DicomVolumeImage.h"
+#include "../../Framework/Oracle/ThreadedOracle.h"
+
+#include <boost/enable_shared_from_this.hpp>
+#include <boost/thread.hpp>
+#include <boost/noncopyable.hpp>
+
+#include <SDL.h>
+
+namespace OrthancStone
+{
+  class OpenGLCompositor;
+  class IVolumeSlicer;
+  class ILayerStyleConfigurator;
+  class DicomStructureSetLoader;
+  class IOracle;
+  class ThreadedOracle;
+  class VolumeSceneLayerSource;
+  class NativeFusionMprApplicationContext;
+  class SdlOpenGLViewport;
+   
+  enum FusionMprGuiTool
+  {
+    FusionMprGuiTool_Rotate = 0,
+    FusionMprGuiTool_Pan,
+    FusionMprGuiTool_Zoom,
+    FusionMprGuiTool_LineMeasure,
+    FusionMprGuiTool_CircleMeasure,
+    FusionMprGuiTool_AngleMeasure,
+    FusionMprGuiTool_EllipseMeasure,
+    FusionMprGuiTool_LAST
+  };
+
+  const char* MeasureToolToString(size_t i);
+
+  static const unsigned int FONT_SIZE_0 = 32;
+  static const unsigned int FONT_SIZE_1 = 24;
+
+  class Scene2D;
+  class UndoStack;
+
+  /**
+  This application subclasses IMessageEmitter to use a mutex before forwarding Oracle messages (that
+  can be sent from multiple threads)
+  */
+  class FusionMprSdlApp : public IObserver
+    , public boost::enable_shared_from_this<FusionMprSdlApp>
+    , public IMessageEmitter
+  {
+  public:
+    // 12 because.
+    FusionMprSdlApp(MessageBroker& broker);
+
+    void PrepareScene();
+    void Run();
+    void SetInfoDisplayMessage(std::string key, std::string value);
+    void DisableTracker();
+
+    Scene2D&       GetScene();
+    const Scene2D& GetScene() const;
+
+    void HandleApplicationEvent(const SDL_Event& event);
+
+    /**
+    This method is called when the scene transform changes. It allows to
+    recompute the visual elements whose content depend upon the scene transform
+    */
+    void OnSceneTransformChanged(
+      const ViewportController::SceneTransformChanged& message);
+
+
+    virtual void EmitMessage(const IObserver& observer,
+      const IMessage& message) ORTHANC_OVERRIDE
+    {
+      try
+      {
+        boost::unique_lock<boost::shared_mutex>  lock(mutex_);
+        oracleObservable_.EmitMessage(observer, message);
+      }
+      catch (Orthanc::OrthancException& e)
+      {
+        LOG(ERROR) << "Exception while emitting a message: " << e.What();
+        throw;
+      }
+    }
+    
+  private:
+#if 1
+    // if threaded (not wasm)
+    MessageBroker& broker_;
+    IObservable oracleObservable_;
+    ThreadedOracle oracle_;
+    boost::shared_mutex mutex_; // to serialize messages from the ThreadedOracle
+#endif
+
+    void SelectNextTool();
+
+    /**
+    This returns a random point in the canvas part of the scene, but in
+    scene coordinates
+    */
+    ScenePoint2D GetRandomPointInScene() const;
+
+    boost::shared_ptr<IFlexiblePointerTracker> TrackerHitTest(const PointerEvent& e);
+
+    boost::shared_ptr<IFlexiblePointerTracker> CreateSuitableTracker(
+      const SDL_Event& event,
+      const PointerEvent& e);
+
+    void TakeScreenshot(
+      const std::string& target,
+      unsigned int canvasWidth,
+      unsigned int canvasHeight);
+
+    /**
+      This adds the command at the top of the undo stack
+    */
+    void Commit(boost::shared_ptr<TrackerCommand> cmd);
+    void Undo();
+    void Redo();
+
+
+    // TODO private
+    void Handle(const DicomVolumeImage::GeometryReadyMessage& message);
+    void Handle(const OracleCommandExceptionMessage& message);
+
+    void SetVolume1(
+      int depth,
+      const boost::shared_ptr<IVolumeSlicer>& volume,
+      ILayerStyleConfigurator* style);
+    
+    void SetVolume2(
+      int depth,
+      const boost::shared_ptr<IVolumeSlicer>& volume,
+      ILayerStyleConfigurator* style);
+
+    void SetStructureSet(
+      int depth, 
+      const boost::shared_ptr<DicomStructureSetLoader>& volume);
+
+
+
+  private:
+    void DisplayFloatingCtrlInfoText(const PointerEvent& e);
+    void DisplayInfoText();
+    void HideInfoText();
+
+  private:
+    CoordinateSystem3D  plane_;
+
+    boost::shared_ptr<VolumeSceneLayerSource>  source1_, source2_, source3_;
+
+    /**
+    WARNING: the measuring tools do store a reference to the scene, and it
+    paramount that the scene gets destroyed AFTER the measurement tools.
+    */
+    boost::shared_ptr<ViewportController> controller_;
+
+    std::map<std::string, std::string> infoTextMap_;
+    boost::shared_ptr<IFlexiblePointerTracker> activeTracker_;
+
+    //static const int LAYER_POSITION = 150;
+
+    int TEXTURE_2x2_1_ZINDEX;
+    int TEXTURE_1x1_ZINDEX;
+    int TEXTURE_2x2_2_ZINDEX;
+    int LINESET_1_ZINDEX;
+    int LINESET_2_ZINDEX;
+    int FLOATING_INFOTEXT_LAYER_ZINDEX;
+    int FIXED_INFOTEXT_LAYER_ZINDEX;
+
+    FusionMprGuiTool currentTool_;
+    boost::shared_ptr<UndoStack> undoStack_;
+    SdlOpenGLViewport viewport_;
+  };
+
+}
+
+
+ 
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/Deprecated/Sdl/Loader.cpp	Tue Apr 07 14:31:28 2020 +0200
@@ -0,0 +1,518 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 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 "../../Framework/Loaders/DicomStructureSetLoader.h"
+#include "../../Framework/Loaders/OrthancMultiframeVolumeLoader.h"
+#include "../../Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h"
+#include "../../Framework/Oracle/SleepOracleCommand.h"
+#include "../../Framework/Oracle/ThreadedOracle.h"
+#include "../../Framework/Scene2D/CairoCompositor.h"
+#include "../../Framework/Scene2D/GrayscaleStyleConfigurator.h"
+#include "../../Framework/Scene2D/LookupTableStyleConfigurator.h"
+#include "../../Framework/StoneInitialization.h"
+#include "../../Framework/Volumes/VolumeSceneLayerSource.h"
+#include "../../Framework/Volumes/DicomVolumeImageMPRSlicer.h"
+#include "../../Framework/Volumes/DicomVolumeImageReslicer.h"
+
+// From Orthanc framework
+#include <Core/Images/ImageProcessing.h>
+#include <Core/Images/PngWriter.h>
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+#include <Core/SystemToolbox.h>
+
+
+namespace OrthancStone
+{
+  class NativeApplicationContext : public IMessageEmitter
+  {
+  private:
+    boost::shared_mutex  mutex_;
+    MessageBroker        broker_;
+    IObservable          oracleObservable_;
+
+  public:
+    NativeApplicationContext() :
+      oracleObservable_(broker_)
+    {
+    }
+
+
+    virtual void EmitMessage(const IObserver& observer,
+                             const IMessage& message) ORTHANC_OVERRIDE
+    {
+      try
+      {
+        boost::unique_lock<boost::shared_mutex>  lock(mutex_);
+        oracleObservable_.EmitMessage(observer, message);
+      }
+      catch (Orthanc::OrthancException& e)
+      {
+        LOG(ERROR) << "Exception while emitting a message: " << e.What();
+      }
+    }
+
+
+    class ReaderLock : public boost::noncopyable
+    {
+    private:
+      NativeApplicationContext&                that_;
+      boost::shared_lock<boost::shared_mutex>  lock_;
+
+    public:
+      ReaderLock(NativeApplicationContext& that) : 
+        that_(that),
+        lock_(that.mutex_)
+      {
+      }
+    };
+
+
+    class WriterLock : public boost::noncopyable
+    {
+    private:
+      NativeApplicationContext&                that_;
+      boost::unique_lock<boost::shared_mutex>  lock_;
+
+    public:
+      WriterLock(NativeApplicationContext& that) : 
+        that_(that),
+        lock_(that.mutex_)
+      {
+      }
+
+      MessageBroker& GetBroker() 
+      {
+        return that_.broker_;
+      }
+
+      IObservable& GetOracleObservable()
+      {
+        return that_.oracleObservable_;
+      }
+    };
+  };
+}
+
+
+
+class Toto : public OrthancStone::IObserver
+{
+private:
+  OrthancStone::CoordinateSystem3D  plane_;
+  OrthancStone::IOracle&            oracle_;
+  OrthancStone::Scene2D             scene_;
+  std::unique_ptr<OrthancStone::VolumeSceneLayerSource>  source1_, source2_, source3_;
+
+
+  void Refresh()
+  {
+    if (source1_.get() != NULL)
+    {
+      source1_->Update(plane_);
+    }
+      
+    if (source2_.get() != NULL)
+    {
+      source2_->Update(plane_);
+    }
+
+    if (source3_.get() != NULL)
+    {
+      source3_->Update(plane_);
+    }
+
+    scene_.FitContent(1024, 768);
+      
+    {
+      OrthancStone::CairoCompositor compositor(scene_, 1024, 768);
+      compositor.Refresh();
+        
+      Orthanc::ImageAccessor accessor;
+      compositor.GetCanvas().GetReadOnlyAccessor(accessor);
+
+      Orthanc::Image tmp(Orthanc::PixelFormat_RGB24, accessor.GetWidth(), accessor.GetHeight(), false);
+      Orthanc::ImageProcessing::Convert(tmp, accessor);
+        
+      static unsigned int count = 0;
+      char buf[64];
+      sprintf(buf, "scene-%06d.png", count++);
+        
+      Orthanc::PngWriter writer;
+      writer.WriteToFile(buf, tmp);
+    }
+  }
+
+  
+  void Handle(const OrthancStone::DicomVolumeImage::GeometryReadyMessage& message)
+  {
+    printf("Geometry ready\n");
+    
+    plane_ = message.GetOrigin().GetGeometry().GetSagittalGeometry();
+    //plane_ = message.GetOrigin().GetGeometry().GetAxialGeometry();
+    //plane_ = message.GetOrigin().GetGeometry().GetCoronalGeometry();
+    plane_.SetOrigin(message.GetOrigin().GetGeometry().GetCoordinates(0.5f, 0.5f, 0.5f));
+
+    Refresh();
+  }
+  
+  
+  void Handle(const OrthancStone::SleepOracleCommand::TimeoutMessage& message)
+  {
+    if (message.GetOrigin().HasPayload())
+    {
+      printf("TIMEOUT! %d\n", dynamic_cast<const Orthanc::SingleValueObject<unsigned int>& >(message.GetOrigin().GetPayload()).GetValue());
+    }
+    else
+    {
+      printf("TIMEOUT\n");
+
+      Refresh();
+
+      /**
+       * The sleep() leads to a crash if the oracle is still running,
+       * while this object is destroyed. Always stop the oracle before
+       * destroying active objects.  (*)
+       **/
+      // boost::this_thread::sleep(boost::posix_time::seconds(2));
+
+      oracle_.Schedule(*this, new OrthancStone::SleepOracleCommand(message.GetOrigin().GetDelay()));
+    }
+  }
+
+  void Handle(const OrthancStone::OrthancRestApiCommand::SuccessMessage& message)
+  {
+    Json::Value v;
+    message.ParseJsonBody(v);
+
+    printf("ICI [%s]\n", v.toStyledString().c_str());
+  }
+
+  void Handle(const OrthancStone::GetOrthancImageCommand::SuccessMessage& message)
+  {
+    printf("IMAGE %dx%d\n", message.GetImage().GetWidth(), message.GetImage().GetHeight());
+  }
+
+  void Handle(const OrthancStone::GetOrthancWebViewerJpegCommand::SuccessMessage& message)
+  {
+    printf("WebViewer %dx%d\n", message.GetImage().GetWidth(), message.GetImage().GetHeight());
+  }
+
+  void Handle(const OrthancStone::OracleCommandExceptionMessage& message)
+  {
+    printf("EXCEPTION: [%s] on command type %d\n", message.GetException().What(), message.GetCommand().GetType());
+
+    switch (message.GetCommand().GetType())
+    {
+      case OrthancStone::IOracleCommand::Type_GetOrthancWebViewerJpeg:
+        printf("URI: [%s]\n", dynamic_cast<const OrthancStone::GetOrthancWebViewerJpegCommand&>
+               (message.GetCommand()).GetUri().c_str());
+        break;
+      
+      default:
+        break;
+    }
+  }
+
+public:
+  Toto(OrthancStone::IOracle& oracle,
+       OrthancStone::IObservable& oracleObservable) :
+    IObserver(oracleObservable.GetBroker()),
+    oracle_(oracle)
+  {
+    oracleObservable.RegisterObserverCallback
+      (new OrthancStone::Callable
+       <Toto, OrthancStone::SleepOracleCommand::TimeoutMessage>(*this, &Toto::Handle));
+
+    oracleObservable.RegisterObserverCallback
+      (new OrthancStone::Callable
+       <Toto, OrthancStone::OrthancRestApiCommand::SuccessMessage>(*this, &Toto::Handle));
+
+    oracleObservable.RegisterObserverCallback
+      (new OrthancStone::Callable
+       <Toto, OrthancStone::GetOrthancImageCommand::SuccessMessage>(*this, &Toto::Handle));
+
+    oracleObservable.RegisterObserverCallback
+      (new OrthancStone::Callable
+       <Toto, OrthancStone::GetOrthancWebViewerJpegCommand::SuccessMessage>(*this, &Toto::Handle));
+
+    oracleObservable.RegisterObserverCallback
+      (new OrthancStone::Callable
+       <Toto, OrthancStone::OracleCommandExceptionMessage>(*this, &Toto::Handle));
+  }
+
+  void SetReferenceLoader(OrthancStone::IObservable& loader)
+  {
+    loader.RegisterObserverCallback
+      (new OrthancStone::Callable
+       <Toto, OrthancStone::DicomVolumeImage::GeometryReadyMessage>(*this, &Toto::Handle));
+  }
+
+  void SetVolume1(int depth,
+                  const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume,
+                  OrthancStone::ILayerStyleConfigurator* style)
+  {
+    source1_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume));
+
+    if (style != NULL)
+    {
+      source1_->SetConfigurator(style);
+    }
+  }
+
+  void SetVolume2(int depth,
+                  const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume,
+                  OrthancStone::ILayerStyleConfigurator* style)
+  {
+    source2_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume));
+
+    if (style != NULL)
+    {
+      source2_->SetConfigurator(style);
+    }
+  }
+
+  void SetStructureSet(int depth,
+                       const boost::shared_ptr<OrthancStone::DicomStructureSetLoader>& volume)
+  {
+    source3_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume));
+  }
+                       
+};
+
+
+void Run(OrthancStone::NativeApplicationContext& context,
+         OrthancStone::ThreadedOracle& oracle)
+{
+  // the oracle has been supplied with the context (as an IEmitter) upon
+  // creation
+  boost::shared_ptr<OrthancStone::DicomVolumeImage>  ct(new OrthancStone::DicomVolumeImage);
+  boost::shared_ptr<OrthancStone::DicomVolumeImage>  dose(new OrthancStone::DicomVolumeImage);
+
+  
+  boost::shared_ptr<Toto> toto;
+  boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> ctLoader;
+  boost::shared_ptr<OrthancStone::OrthancMultiframeVolumeLoader> doseLoader;
+  boost::shared_ptr<OrthancStone::DicomStructureSetLoader>  rtstructLoader;
+
+  {
+    OrthancStone::NativeApplicationContext::WriterLock lock(context);
+    toto.reset(new Toto(oracle, lock.GetOracleObservable()));
+
+    // the oracle is used to schedule commands
+    // the oracleObservable is used by the loaders to:
+    // - request the broker (lifetime mgmt)
+    // - register the loader callbacks (called indirectly by the oracle)
+    ctLoader.reset(new OrthancStone::OrthancSeriesVolumeProgressiveLoader(ct, oracle, lock.GetOracleObservable()));
+    doseLoader.reset(new OrthancStone::OrthancMultiframeVolumeLoader(dose, oracle, lock.GetOracleObservable()));
+    rtstructLoader.reset(new OrthancStone::DicomStructureSetLoader(oracle, lock.GetOracleObservable()));
+  }
+
+
+  //toto->SetReferenceLoader(*ctLoader);
+  toto->SetReferenceLoader(*doseLoader);
+
+
+#if 1
+  toto->SetVolume1(0, ctLoader, new OrthancStone::GrayscaleStyleConfigurator);
+#else
+  {
+    boost::shared_ptr<OrthancStone::IVolumeSlicer> reslicer(new OrthancStone::DicomVolumeImageReslicer(ct));
+    toto->SetVolume1(0, reslicer, new OrthancStone::GrayscaleStyleConfigurator);
+  }
+#endif  
+  
+
+  {
+    std::unique_ptr<OrthancStone::LookupTableStyleConfigurator> config(new OrthancStone::LookupTableStyleConfigurator);
+    config->SetLookupTable(Orthanc::EmbeddedResources::COLORMAP_HOT);
+
+    boost::shared_ptr<OrthancStone::DicomVolumeImageMPRSlicer> tmp(new OrthancStone::DicomVolumeImageMPRSlicer(dose));
+    toto->SetVolume2(1, tmp, config.release());
+  }
+
+  toto->SetStructureSet(2, rtstructLoader);
+  
+  oracle.Schedule(*toto, new OrthancStone::SleepOracleCommand(100));
+
+  if (0)
+  {
+    Json::Value v = Json::objectValue;
+    v["Level"] = "Series";
+    v["Query"] = Json::objectValue;
+
+    std::unique_ptr<OrthancStone::OrthancRestApiCommand>  command(new OrthancStone::OrthancRestApiCommand);
+    command->SetMethod(Orthanc::HttpMethod_Post);
+    command->SetUri("/tools/find");
+    command->SetBody(v);
+
+    oracle.Schedule(*toto, command.release());
+  }
+  
+  if(0)
+  {
+    if (0)
+    {
+      std::unique_ptr<OrthancStone::GetOrthancImageCommand>  command(new OrthancStone::GetOrthancImageCommand);
+      command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Jpeg)));
+      command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/preview");
+      oracle.Schedule(*toto, command.release());
+    }
+    
+    if (0)
+    {
+      std::unique_ptr<OrthancStone::GetOrthancImageCommand>  command(new OrthancStone::GetOrthancImageCommand);
+      command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Png)));
+      command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/preview");
+      oracle.Schedule(*toto, command.release());
+    }
+    
+    if (0)
+    {
+      std::unique_ptr<OrthancStone::GetOrthancImageCommand>  command(new OrthancStone::GetOrthancImageCommand);
+      command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Png)));
+      command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16");
+      oracle.Schedule(*toto, command.release());
+    }
+    
+    if (0)
+    {
+      std::unique_ptr<OrthancStone::GetOrthancImageCommand>  command(new OrthancStone::GetOrthancImageCommand);
+      command->SetHttpHeader("Accept-Encoding", "gzip");
+      command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Pam)));
+      command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16");
+      oracle.Schedule(*toto, command.release());
+    }
+    
+    if (0)
+    {
+      std::unique_ptr<OrthancStone::GetOrthancImageCommand>  command(new OrthancStone::GetOrthancImageCommand);
+      command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Pam)));
+      command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16");
+      oracle.Schedule(*toto, command.release());
+    }
+
+    if (0)
+    {
+      std::unique_ptr<OrthancStone::GetOrthancWebViewerJpegCommand>  command(new OrthancStone::GetOrthancWebViewerJpegCommand);
+      command->SetHttpHeader("Accept-Encoding", "gzip");
+      command->SetInstance("e6c7c20b-c9f65d7e-0d76f2e2-830186f2-3e3c600e");
+      command->SetQuality(90);
+      oracle.Schedule(*toto, command.release());
+    }
+
+
+    if (0)
+    {
+      for (unsigned int i = 0; i < 10; i++)
+      {
+        std::unique_ptr<OrthancStone::SleepOracleCommand> command(new OrthancStone::SleepOracleCommand(i * 1000));
+        command->SetPayload(new Orthanc::SingleValueObject<unsigned int>(42 * i));
+        oracle.Schedule(*toto, command.release());
+      }
+    }
+  }
+  
+  // 2017-11-17-Anonymized
+#if 0
+  // BGO data
+  ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa");  // CT
+  doseLoader->LoadInstance("830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb");  // RT-DOSE
+  //rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9");  // RT-STRUCT
+#else
+  //ctLoader->LoadSeries("cb3ea4d1-d08f3856-ad7b6314-74d88d77-60b05618");  // CT
+  //doseLoader->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6");  // RT-DOSE
+  //rtstructLoader->LoadInstance("83d9c0c3-913a7fee-610097d7-cbf0522d-fd75bee6");  // RT-STRUCT
+
+  // 2017-05-16
+  ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa");  // CT
+  doseLoader->LoadInstance("eac822ef-a395f94e-e8121fe0-8411fef8-1f7bffad");  // RT-DOSE
+  rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9");  // RT-STRUCT
+#endif
+  // 2015-01-28-Multiframe
+  //doseLoader->LoadInstance("88f71e2a-5fad1c61-96ed14d6-5b3d3cf7-a5825279");  // Multiframe CT
+  
+  // Delphine
+  //ctLoader->LoadSeries("5990e39c-51e5f201-fe87a54c-31a55943-e59ef80e");  // CT
+  //ctLoader->LoadSeries("67f1b334-02c16752-45026e40-a5b60b6b-030ecab5");  // Lung 1/10mm
+
+
+  {
+    LOG(WARNING) << "...Waiting for Ctrl-C...";
+
+    oracle.Start();
+
+    Orthanc::SystemToolbox::ServerBarrier();
+
+    /**
+     * WARNING => The oracle must be stopped BEFORE the objects using
+     * it are destroyed!!! This forces to wait for the completion of
+     * the running callback methods. Otherwise, the callbacks methods
+     * might still be running while their parent object is destroyed,
+     * resulting in crashes. This is very visible if adding a sleep(),
+     * as in (*).
+     **/
+
+    oracle.Stop();
+  }
+}
+
+
+
+/**
+ * IMPORTANT: The full arguments to "main()" are needed for SDL on
+ * Windows. Otherwise, one gets the linking error "undefined reference
+ * to `SDL_main'". https://wiki.libsdl.org/FAQWindows
+ **/
+int main(int argc, char* argv[])
+{
+  OrthancStone::StoneInitialize();
+  //Orthanc::Logging::EnableInfoLevel(true);
+
+  try
+  {
+    OrthancStone::NativeApplicationContext context;
+
+    OrthancStone::ThreadedOracle oracle(context);
+    //oracle.SetThreadsCount(1);
+
+    {
+      Orthanc::WebServiceParameters p;
+      //p.SetUrl("http://localhost:8043/");
+      p.SetCredentials("orthanc", "orthanc");
+      oracle.SetOrthancParameters(p);
+    }
+
+    //oracle.Start();
+
+    Run(context, oracle);
+    
+    //oracle.Stop();
+  }
+  catch (Orthanc::OrthancException& e)
+  {
+    LOG(ERROR) << "EXCEPTION: " << e.What();
+  }
+
+  OrthancStone::StoneFinalize();
+
+  return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/Deprecated/Sdl/RadiographyEditor.cpp	Tue Apr 07 14:31:28 2020 +0200
@@ -0,0 +1,267 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 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 "../Shared/RadiographyEditorApp.h"
+
+// From Stone
+#include "../../Framework/Oracle/SleepOracleCommand.h"
+#include "../../Framework/Oracle/ThreadedOracle.h"
+#include "../../Applications/Sdl/SdlOpenGLWindow.h"
+#include "../../Framework/Scene2D/OpenGLCompositor.h"
+#include "../../Framework/Scene2D/CairoCompositor.h"
+#include "../../Framework/Scene2D/ColorTextureSceneLayer.h"
+#include "../../Framework/Scene2D/OpenGLCompositor.h"
+#include "../../Framework/StoneInitialization.h"
+
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+
+
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+
+#include <SDL.h>
+#include <stdio.h>
+
+using namespace OrthancStone;
+
+namespace OrthancStone
+{
+  class NativeApplicationContext : public IMessageEmitter
+  {
+  private:
+    boost::shared_mutex  mutex_;
+    MessageBroker        broker_;
+    IObservable          oracleObservable_;
+
+  public:
+    NativeApplicationContext() :
+      oracleObservable_(broker_)
+    {
+    }
+
+
+    virtual void EmitMessage(const IObserver& observer,
+                             const IMessage& message) ORTHANC_OVERRIDE
+    {
+      try
+      {
+        boost::unique_lock<boost::shared_mutex>  lock(mutex_);
+        oracleObservable_.EmitMessage(observer, message);
+      }
+      catch (Orthanc::OrthancException& e)
+      {
+        LOG(ERROR) << "Exception while emitting a message: " << e.What();
+      }
+    }
+
+
+    class ReaderLock : public boost::noncopyable
+    {
+    private:
+      NativeApplicationContext&                that_;
+      boost::shared_lock<boost::shared_mutex>  lock_;
+
+    public:
+      ReaderLock(NativeApplicationContext& that) :
+        that_(that),
+        lock_(that.mutex_)
+      {
+      }
+    };
+
+
+    class WriterLock : public boost::noncopyable
+    {
+    private:
+      NativeApplicationContext&                that_;
+      boost::unique_lock<boost::shared_mutex>  lock_;
+
+    public:
+      WriterLock(NativeApplicationContext& that) :
+        that_(that),
+        lock_(that.mutex_)
+      {
+      }
+
+      MessageBroker& GetBroker()
+      {
+        return that_.broker_;
+      }
+
+      IObservable& GetOracleObservable()
+      {
+        return that_.oracleObservable_;
+      }
+    };
+  };
+}
+
+class OpenGlSdlCompositorFactory : public ICompositorFactory
+{
+  OpenGL::IOpenGLContext& openGlContext_;
+
+public:
+  OpenGlSdlCompositorFactory(OpenGL::IOpenGLContext& openGlContext) :
+    openGlContext_(openGlContext)
+  {}
+
+  ICompositor* GetCompositor(const Scene2D& scene)
+  {
+
+    OpenGLCompositor* compositor = new OpenGLCompositor(openGlContext_, scene);
+    compositor->SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT,
+                         FONT_SIZE_0, Orthanc::Encoding_Latin1);
+    compositor->SetFont(1, Orthanc::EmbeddedResources::UBUNTU_FONT,
+                         FONT_SIZE_1, Orthanc::Encoding_Latin1);
+    return compositor;
+  }
+};
+
+static void GLAPIENTRY
+OpenGLMessageCallback(GLenum source,
+                      GLenum type,
+                      GLuint id,
+                      GLenum severity,
+                      GLsizei length,
+                      const GLchar* message,
+                      const void* userParam)
+{
+  if (severity != GL_DEBUG_SEVERITY_NOTIFICATION)
+  {
+    fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
+            (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""),
+            type, severity, message);
+  }
+}
+
+
+/**
+ * IMPORTANT: The full arguments to "main()" are needed for SDL on
+ * Windows. Otherwise, one gets the linking error "undefined reference
+ * to `SDL_main'". https://wiki.libsdl.org/FAQWindows
+ **/
+int main(int argc, char* argv[])
+{
+  using namespace OrthancStone;
+
+  StoneInitialize();
+  Orthanc::Logging::EnableInfoLevel(true);
+  //  Orthanc::Logging::EnableTraceLevel(true);
+
+  try
+  {
+    OrthancStone::NativeApplicationContext context;
+    OrthancStone::NativeApplicationContext::WriterLock lock(context);
+    OrthancStone::ThreadedOracle oracle(context);
+
+    // False means we do NOT let Windows treat this as a legacy application
+    // that needs to be scaled
+    SdlOpenGLWindow window("Hello", 1024, 1024, false);
+
+    glEnable(GL_DEBUG_OUTPUT);
+    glDebugMessageCallback(OpenGLMessageCallback, 0);
+
+    std::unique_ptr<OpenGlSdlCompositorFactory> compositorFactory(new OpenGlSdlCompositorFactory(window));
+    boost::shared_ptr<RadiographyEditorApp> app(new RadiographyEditorApp(oracle, lock.GetOracleObservable(), compositorFactory.release()));
+    app->PrepareScene();
+    app->FitContent(window.GetCanvasWidth(), window.GetCanvasHeight());
+
+    bool stopApplication = false;
+
+    while (!stopApplication)
+    {
+      app->Refresh();
+
+      SDL_Event event;
+      while (!stopApplication && SDL_PollEvent(&event))
+      {
+        OrthancStone::KeyboardModifiers modifiers = OrthancStone::KeyboardModifiers_None;
+        if (event.key.keysym.mod & KMOD_CTRL)
+          modifiers = static_cast<OrthancStone::KeyboardModifiers>(static_cast<int>(modifiers) | static_cast<int>(OrthancStone::KeyboardModifiers_Control));
+        if (event.key.keysym.mod & KMOD_ALT)
+          modifiers = static_cast<OrthancStone::KeyboardModifiers>(static_cast<int>(modifiers) | static_cast<int>(OrthancStone::KeyboardModifiers_Alt));
+        if (event.key.keysym.mod & KMOD_SHIFT)
+          modifiers = static_cast<OrthancStone::KeyboardModifiers>(static_cast<int>(modifiers) | static_cast<int>(OrthancStone::KeyboardModifiers_Shift));
+
+        OrthancStone::MouseButton button;
+        if (event.button.button == SDL_BUTTON_LEFT)
+          button = OrthancStone::MouseButton_Left;
+        else if (event.button.button == SDL_BUTTON_MIDDLE)
+          button = OrthancStone::MouseButton_Middle;
+        else if (event.button.button == SDL_BUTTON_RIGHT)
+          button = OrthancStone::MouseButton_Right;
+
+        if (event.type == SDL_QUIT)
+        {
+          stopApplication = true;
+          break;
+        }
+        else if (event.type == SDL_WINDOWEVENT &&
+                 event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
+        {
+          app->DisableTracker(); // was: tracker.reset(NULL);
+          app->UpdateSize();
+        }
+        else if (event.type == SDL_KEYDOWN &&
+                 event.key.repeat == 0 /* Ignore key bounce */)
+        {
+          switch (event.key.keysym.sym)
+          {
+          case SDLK_f:
+            window.GetWindow().ToggleMaximize();
+            break;
+
+          case SDLK_q:
+            stopApplication = true;
+            break;
+          default:
+          {
+            app->OnKeyPressed(event.key.keysym.sym, modifiers);
+           }
+          }
+        }
+        else if (event.type == SDL_MOUSEBUTTONDOWN)
+        {
+          app->OnMouseDown(event.button.x, event.button.y, modifiers, button);
+        }
+        else if (event.type == SDL_MOUSEMOTION)
+        {
+          app->OnMouseMove(event.button.x, event.button.y, modifiers);
+        }
+        else if (event.type == SDL_MOUSEBUTTONUP)
+        {
+          app->OnMouseUp(event.button.x, event.button.y, modifiers, button);
+        }
+      }
+      SDL_Delay(1);
+    }
+  }
+  catch (Orthanc::OrthancException& e)
+  {
+    LOG(ERROR) << "EXCEPTION: " << e.What();
+  }
+
+  StoneFinalize();
+
+  return 0;
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/Deprecated/Sdl/TrackerSample.cpp	Tue Apr 07 14:31:28 2020 +0200
@@ -0,0 +1,93 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 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 "TrackerSampleApp.h"
+
+ // From Stone
+#include "../../Framework/OpenGL/SdlOpenGLContext.h"
+#include "../../Framework/Scene2D/CairoCompositor.h"
+#include "../../Framework/Scene2D/ColorTextureSceneLayer.h"
+#include "../../Framework/Scene2D/OpenGLCompositor.h"
+#include "../../Framework/StoneInitialization.h"
+
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+
+
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+
+#include <SDL.h>
+#include <stdio.h>
+
+/*
+TODO:
+
+- to decouple the trackers from the sample, we need to supply them with
+  the scene rather than the app
+
+- in order to do that, we need a GetNextFreeZIndex function (or something 
+  along those lines) in the scene object
+
+*/
+
+boost::weak_ptr<OrthancStone::TrackerSampleApp> g_app;
+
+void TrackerSample_SetInfoDisplayMessage(std::string key, std::string value)
+{
+  boost::shared_ptr<OrthancStone::TrackerSampleApp> app = g_app.lock();
+  if (app)
+  {
+    app->SetInfoDisplayMessage(key, value);
+  }
+}
+
+/**
+ * IMPORTANT: The full arguments to "main()" are needed for SDL on
+ * Windows. Otherwise, one gets the linking error "undefined reference
+ * to `SDL_main'". https://wiki.libsdl.org/FAQWindows
+ **/
+int main(int argc, char* argv[])
+{
+  using namespace OrthancStone;
+
+  StoneInitialize();
+  Orthanc::Logging::EnableInfoLevel(true);
+//  Orthanc::Logging::EnableTraceLevel(true);
+
+  try
+  {
+    MessageBroker broker;
+    boost::shared_ptr<TrackerSampleApp> app(new TrackerSampleApp(broker));
+    g_app = app;
+    app->PrepareScene();
+    app->Run();
+  }
+  catch (Orthanc::OrthancException& e)
+  {
+    LOG(ERROR) << "EXCEPTION: " << e.What();
+  }
+
+  StoneFinalize();
+
+  return 0;
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/Deprecated/Sdl/TrackerSampleApp.cpp	Tue Apr 07 14:31:28 2020 +0200
@@ -0,0 +1,733 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 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 "TrackerSampleApp.h"
+
+#include "../../Framework/OpenGL/SdlOpenGLContext.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/Scene2DViewport/UndoStack.h"
+#include "../../Framework/Scene2DViewport/CreateAngleMeasureTracker.h"
+#include "../../Framework/Scene2DViewport/CreateLineMeasureTracker.h"
+#include "../../Framework/StoneInitialization.h"
+
+// From Orthanc framework
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+#include <Core/Images/Image.h>
+#include <Core/Images/ImageProcessing.h>
+#include <Core/Images/PngWriter.h>
+
+#include <boost/ref.hpp>
+#include <boost/make_shared.hpp>
+#include <SDL.h>
+
+#include <stdio.h>
+
+namespace OrthancStone
+{
+  const char* MeasureToolToString(size_t i)
+  {
+    static const char* descs[] = {
+      "GuiTool_Rotate",
+      "GuiTool_Pan",
+      "GuiTool_Zoom",
+      "GuiTool_LineMeasure",
+      "GuiTool_CircleMeasure",
+      "GuiTool_AngleMeasure",
+      "GuiTool_EllipseMeasure",
+      "GuiTool_LAST"
+    };
+    if (i >= GuiTool_LAST)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Wrong tool index");
+    }
+    return descs[i];
+  }
+
+  void TrackerSampleApp::SelectNextTool()
+  {
+    currentTool_ = static_cast<GuiTool>(currentTool_ + 1);
+    if (currentTool_ == GuiTool_LAST)
+      currentTool_ = static_cast<GuiTool>(0);;
+    printf("Current tool is now: %s\n", MeasureToolToString(currentTool_));
+  }
+
+  void TrackerSampleApp::DisplayInfoText()
+  {
+    // do not try to use stuff too early!
+    std::stringstream msg;
+	
+	for (std::map<std::string, std::string>::const_iterator kv = infoTextMap_.begin();
+		kv != infoTextMap_.end(); ++kv)
+    {
+      msg << kv->first << " : " << kv->second << std::endl;
+    }
+	std::string msgS = msg.str();
+
+    TextSceneLayer* layerP = NULL;
+    if (controller_->GetScene().HasLayer(FIXED_INFOTEXT_LAYER_ZINDEX))
+    {
+      TextSceneLayer& layer = dynamic_cast<TextSceneLayer&>(
+        controller_->GetScene().GetLayer(FIXED_INFOTEXT_LAYER_ZINDEX));
+      layerP = &layer;
+    }
+    else
+    {
+      std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer);
+      layerP = layer.get();
+      layer->SetColor(0, 255, 0);
+      layer->SetFontIndex(1);
+      layer->SetBorder(20);
+      layer->SetAnchor(BitmapAnchor_TopLeft);
+      //layer->SetPosition(0,0);
+      controller_->GetScene().SetLayer(FIXED_INFOTEXT_LAYER_ZINDEX, layer.release());
+    }
+    // position the fixed info text in the upper right corner
+    layerP->SetText(msgS.c_str());
+    double cX = GetCompositor().GetCanvasWidth() * (-0.5);
+    double cY = GetCompositor().GetCanvasHeight() * (-0.5);
+    controller_->GetScene().GetCanvasToSceneTransform().Apply(cX,cY);
+    layerP->SetPosition(cX, cY);
+  }
+
+  void TrackerSampleApp::DisplayFloatingCtrlInfoText(const PointerEvent& e)
+  {
+    ScenePoint2D p = e.GetMainPosition().Apply(controller_->GetScene().GetCanvasToSceneTransform());
+
+    char buf[128];
+    sprintf(buf, "S:(%0.02f,%0.02f) C:(%0.02f,%0.02f)", 
+      p.GetX(), p.GetY(), 
+      e.GetMainPosition().GetX(), e.GetMainPosition().GetY());
+
+    if (controller_->GetScene().HasLayer(FLOATING_INFOTEXT_LAYER_ZINDEX))
+    {
+      TextSceneLayer& layer =
+        dynamic_cast<TextSceneLayer&>(controller_->GetScene().GetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX));
+      layer.SetText(buf);
+      layer.SetPosition(p.GetX(), p.GetY());
+    }
+    else
+    {
+      std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer);
+      layer->SetColor(0, 255, 0);
+      layer->SetText(buf);
+      layer->SetBorder(20);
+      layer->SetAnchor(BitmapAnchor_BottomCenter);
+      layer->SetPosition(p.GetX(), p.GetY());
+      controller_->GetScene().SetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX, layer.release());
+    }
+  }
+
+  void TrackerSampleApp::HideInfoText()
+  {
+    controller_->GetScene().DeleteLayer(FLOATING_INFOTEXT_LAYER_ZINDEX);
+  }
+
+  ScenePoint2D TrackerSampleApp::GetRandomPointInScene() const
+  {
+    unsigned int w = GetCompositor().GetCanvasWidth();
+    LOG(TRACE) << "GetCompositor().GetCanvasWidth() = " << 
+      GetCompositor().GetCanvasWidth();
+    unsigned int h = GetCompositor().GetCanvasHeight();
+    LOG(TRACE) << "GetCompositor().GetCanvasHeight() = " << 
+      GetCompositor().GetCanvasHeight();
+
+    if ((w >= RAND_MAX) || (h >= RAND_MAX))
+      LOG(WARNING) << "Canvas is too big : tools will not be randomly placed";
+
+    int x = rand() % w;
+    int y = rand() % h;
+    LOG(TRACE) << "random x = " << x << "random y = " << y;
+
+    ScenePoint2D p = controller_->GetViewport().GetPixelCenterCoordinates(x, y);
+    LOG(TRACE) << "--> p.GetX() = " << p.GetX() << " p.GetY() = " << p.GetY();
+
+    ScenePoint2D r = p.Apply(controller_->GetScene().GetCanvasToSceneTransform());
+    LOG(TRACE) << "--> r.GetX() = " << r.GetX() << " r.GetY() = " << r.GetY();
+    return r;
+  }
+
+  void TrackerSampleApp::CreateRandomMeasureTool()
+  {
+    static bool srandCalled = false;
+    if (!srandCalled)
+    {
+      srand(42);
+      srandCalled = true;
+    }
+
+    int i = rand() % 2;
+    LOG(TRACE) << "random i = " << i;
+    switch (i)
+    {
+    case 0:
+      // line measure
+      {
+        boost::shared_ptr<CreateLineMeasureCommand> cmd = 
+          boost::make_shared<CreateLineMeasureCommand>(
+		  boost::ref(IObserver::GetBroker()),
+            controller_,
+            GetRandomPointInScene());
+        cmd->SetEnd(GetRandomPointInScene());
+        controller_->PushCommand(cmd);
+      }
+      break;
+    case 1:
+      // angle measure
+      {
+      boost::shared_ptr<CreateAngleMeasureCommand> cmd =
+        boost::make_shared<CreateAngleMeasureCommand>(
+          boost::ref(IObserver::GetBroker()),
+          controller_,
+          GetRandomPointInScene());
+        cmd->SetCenter(GetRandomPointInScene());
+        cmd->SetSide2End(GetRandomPointInScene());
+        controller_->PushCommand(cmd);
+      }
+      break;
+    }
+  }
+
+  void TrackerSampleApp::HandleApplicationEvent(
+    const SDL_Event & event)
+  {
+    DisplayInfoText();
+
+    if (event.type == SDL_MOUSEMOTION)
+    {
+      int scancodeCount = 0;
+      const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount);
+
+      if (activeTracker_.get() == NULL &&
+        SDL_SCANCODE_LALT < scancodeCount &&
+        keyboardState[SDL_SCANCODE_LALT])
+      {
+        // The "left-ctrl" key is down, while no tracker is present
+        // Let's display the info text
+        PointerEvent e;
+        e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(
+          event.button.x, event.button.y));
+
+        DisplayFloatingCtrlInfoText(e);
+      }
+      else if (activeTracker_.get() != NULL)
+      {
+        HideInfoText();
+        //LOG(TRACE) << "(event.type == SDL_MOUSEMOTION)";
+        if (activeTracker_.get() != NULL)
+        {
+          //LOG(TRACE) << "(activeTracker_.get() != NULL)";
+          PointerEvent e;
+          e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(
+            event.button.x, event.button.y));
+          
+          //LOG(TRACE) << "event.button.x = " << event.button.x << "     " <<
+          //  "event.button.y = " << event.button.y;
+          LOG(TRACE) << "activeTracker_->PointerMove(e); " <<
+            e.GetMainPosition().GetX() << " " << e.GetMainPosition().GetY();
+          
+          activeTracker_->PointerMove(e);
+          if (!activeTracker_->IsAlive())
+            activeTracker_.reset();
+        }
+      }
+      else
+      {
+        HideInfoText();
+
+        PointerEvent e;
+        e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(event.button.x, event.button.y));
+
+        ScenePoint2D scenePos = e.GetMainPosition().Apply(
+          controller_->GetScene().GetCanvasToSceneTransform());
+        //auto measureTools = GetController()->HitTestMeasureTools(scenePos);
+        //LOG(TRACE) << "# of hit tests: " << measureTools.size();
+        
+        // this returns the collection of measuring tools where hit test is true
+        std::vector<boost::shared_ptr<MeasureTool> > measureTools = controller_->HitTestMeasureTools(scenePos);
+
+        // let's refresh the measuring tools highlighted state
+        // first let's tag them as "unhighlighted"
+        controller_->ResetMeasuringToolsHighlight();
+
+        // then immediately take the first one and ask it to highlight the 
+        // measuring tool UI part that is hot
+        if (measureTools.size() > 0)
+        {
+          measureTools[0]->Highlight(scenePos);
+        }
+      }
+    }
+    else if (event.type == SDL_MOUSEBUTTONUP)
+    {
+      if (activeTracker_)
+      {
+        PointerEvent e;
+        e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(event.button.x, event.button.y));
+        activeTracker_->PointerUp(e);
+        if (!activeTracker_->IsAlive())
+          activeTracker_.reset();
+      }
+    }
+    else if (event.type == SDL_MOUSEBUTTONDOWN)
+    {
+      PointerEvent e;
+      e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(
+        event.button.x, event.button.y));
+      if (activeTracker_)
+      {
+        activeTracker_->PointerDown(e);
+        if (!activeTracker_->IsAlive())
+          activeTracker_.reset();
+      }
+      else
+      {
+        // we ATTEMPT to create a tracker if need be
+        activeTracker_ = CreateSuitableTracker(event, e);
+      }
+    }
+    else if (event.type == SDL_KEYDOWN &&
+      event.key.repeat == 0 /* Ignore key bounce */)
+    {
+      switch (event.key.keysym.sym)
+      {
+      case SDLK_ESCAPE:
+        if (activeTracker_)
+        {
+          activeTracker_->Cancel();
+          if (!activeTracker_->IsAlive())
+            activeTracker_.reset();
+        }
+        break;
+
+      case SDLK_t:
+        if (!activeTracker_)
+          SelectNextTool();
+        else
+        {
+          LOG(WARNING) << "You cannot change the active tool when an interaction"
+            " is taking place";
+        }
+        break;
+
+      case SDLK_m:
+        CreateRandomMeasureTool();
+        break;
+      case SDLK_s:
+        controller_->FitContent(GetCompositor().GetCanvasWidth(),
+          GetCompositor().GetCanvasHeight());
+        break;
+
+      case SDLK_z:
+        LOG(TRACE) << "SDLK_z has been pressed. event.key.keysym.mod == " << event.key.keysym.mod;
+        if (event.key.keysym.mod & KMOD_CTRL)
+        {
+          if (controller_->CanUndo())
+          {
+            LOG(TRACE) << "Undoing...";
+            controller_->Undo();
+          }
+          else
+          {
+            LOG(WARNING) << "Nothing to undo!!!";
+          }
+        }
+        break;
+
+      case SDLK_y:
+        LOG(TRACE) << "SDLK_y has been pressed. event.key.keysym.mod == " << event.key.keysym.mod;
+        if (event.key.keysym.mod & KMOD_CTRL)
+        {
+          if (controller_->CanRedo())
+          {
+            LOG(TRACE) << "Redoing...";
+            controller_->Redo();
+          }
+          else
+          {
+            LOG(WARNING) << "Nothing to redo!!!";
+          }
+        }
+        break;
+
+      case SDLK_c:
+        TakeScreenshot(
+          "screenshot.png",
+          GetCompositor().GetCanvasWidth(),
+          GetCompositor().GetCanvasHeight());
+        break;
+
+      default:
+        break;
+      }
+    }
+  }
+
+
+  void TrackerSampleApp::OnSceneTransformChanged(
+    const ViewportController::SceneTransformChanged& message)
+  {
+    DisplayInfoText();
+  }
+
+  boost::shared_ptr<IFlexiblePointerTracker> TrackerSampleApp::CreateSuitableTracker(
+    const SDL_Event & event,
+    const PointerEvent & e)
+  {
+    using namespace Orthanc;
+
+    switch (event.button.button)
+    {
+    case SDL_BUTTON_MIDDLE:
+      return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker
+        (controller_, e));
+
+    case SDL_BUTTON_RIGHT:
+      return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker
+        (controller_, e, GetCompositor().GetCanvasHeight()));
+
+    case SDL_BUTTON_LEFT:
+    {
+      //LOG(TRACE) << "CreateSuitableTracker: case SDL_BUTTON_LEFT:";
+      // TODO: we need to iterate on the set of measuring tool and perform
+      // a hit test to check if a tracker needs to be created for edition.
+      // Otherwise, depending upon the active tool, we might want to create
+      // a "measuring tool creation" tracker
+
+      // TODO: if there are conflicts, we should prefer a tracker that 
+      // pertains to the type of measuring tool currently selected (TBD?)
+      boost::shared_ptr<IFlexiblePointerTracker> hitTestTracker = TrackerHitTest(e);
+
+      if (hitTestTracker != NULL)
+      {
+        //LOG(TRACE) << "hitTestTracker != NULL";
+        return hitTestTracker;
+      }
+      else
+      {
+        switch (currentTool_)
+        {
+        case GuiTool_Rotate:
+          //LOG(TRACE) << "Creating RotateSceneTracker";
+          return boost::shared_ptr<IFlexiblePointerTracker>(new RotateSceneTracker(
+            controller_, e));
+        case GuiTool_Pan:
+          return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker(
+            controller_, e));
+        case GuiTool_Zoom:
+          return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker(
+            controller_, e, GetCompositor().GetCanvasHeight()));
+        //case GuiTool_AngleMeasure:
+        //  return new AngleMeasureTracker(GetScene(), e);
+        //case GuiTool_CircleMeasure:
+        //  return new CircleMeasureTracker(GetScene(), e);
+        //case GuiTool_EllipseMeasure:
+        //  return new EllipseMeasureTracker(GetScene(), e);
+        case GuiTool_LineMeasure:
+          return boost::shared_ptr<IFlexiblePointerTracker>(new CreateLineMeasureTracker(
+            IObserver::GetBroker(), controller_, e));
+        case GuiTool_AngleMeasure:
+          return boost::shared_ptr<IFlexiblePointerTracker>(new CreateAngleMeasureTracker(
+            IObserver::GetBroker(), controller_, e));
+        case GuiTool_CircleMeasure:
+          LOG(ERROR) << "Not implemented yet!";
+          return boost::shared_ptr<IFlexiblePointerTracker>();
+        case GuiTool_EllipseMeasure:
+          LOG(ERROR) << "Not implemented yet!";
+          return boost::shared_ptr<IFlexiblePointerTracker>();
+        default:
+          throw OrthancException(ErrorCode_InternalError, "Wrong tool!");
+        }
+      }
+    }
+    default:
+      return boost::shared_ptr<IFlexiblePointerTracker>();
+    }
+  }
+
+
+  TrackerSampleApp::TrackerSampleApp(MessageBroker& broker) : IObserver(broker)
+    , currentTool_(GuiTool_Rotate)
+    , undoStack_(new UndoStack)
+    , viewport_("Hello", 1024, 1024, false) // False means we do NOT let Windows treat this as a legacy application that needs to be scaled
+  {
+    controller_ = boost::shared_ptr<ViewportController>(
+      new ViewportController(undoStack_, broker, viewport_));
+
+    controller_->RegisterObserverCallback(
+      new Callable<TrackerSampleApp, ViewportController::SceneTransformChanged>
+      (*this, &TrackerSampleApp::OnSceneTransformChanged));
+
+    TEXTURE_2x2_1_ZINDEX = 1;
+    TEXTURE_1x1_ZINDEX = 2;
+    TEXTURE_2x2_2_ZINDEX = 3;
+    LINESET_1_ZINDEX = 4;
+    LINESET_2_ZINDEX = 5;
+    FLOATING_INFOTEXT_LAYER_ZINDEX = 6;
+    FIXED_INFOTEXT_LAYER_ZINDEX = 7;
+  }
+
+  void TrackerSampleApp::PrepareScene()
+  {
+    // Texture of 2x2 size
+    {
+      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;
+
+      controller_->GetScene().SetLayer(TEXTURE_2x2_1_ZINDEX, new ColorTextureSceneLayer(i));
+
+      std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i));
+      l->SetOrigin(-3, 2);
+      l->SetPixelSpacing(1.5, 1);
+      l->SetAngle(20.0 / 180.0 * M_PI);
+      controller_->GetScene().SetLayer(TEXTURE_2x2_2_ZINDEX, l.release());
+    }
+
+    // Texture of 1x1 size
+    {
+      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::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i));
+      l->SetOrigin(-2, 1);
+      l->SetAngle(20.0 / 180.0 * M_PI);
+      controller_->GetScene().SetLayer(TEXTURE_1x1_ZINDEX, l.release());
+    }
+
+    // Some lines
+    {
+      std::unique_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, 255, 0, 0);
+
+      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, 0, 255, 0);
+
+      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, 0, 0, 255);
+
+      controller_->GetScene().SetLayer(LINESET_1_ZINDEX, layer.release());
+    }
+
+    // Some text
+    {
+      std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer);
+      layer->SetText("Hello");
+      controller_->GetScene().SetLayer(LINESET_2_ZINDEX, layer.release());
+    }
+  }
+
+
+  void TrackerSampleApp::DisableTracker()
+  {
+    if (activeTracker_)
+    {
+      activeTracker_->Cancel();
+      activeTracker_.reset();
+    }
+  }
+
+  void TrackerSampleApp::TakeScreenshot(const std::string& target,
+    unsigned int canvasWidth,
+    unsigned int canvasHeight)
+  {
+    CairoCompositor compositor(controller_->GetScene(), canvasWidth, canvasHeight);
+    compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, FONT_SIZE_0, Orthanc::Encoding_Latin1);
+    compositor.Refresh();
+
+    Orthanc::ImageAccessor canvas;
+    compositor.GetCanvas().GetReadOnlyAccessor(canvas);
+
+    Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false);
+    Orthanc::ImageProcessing::Convert(png, canvas);
+
+    Orthanc::PngWriter writer;
+    writer.WriteToFile(target, png);
+  }
+
+
+  boost::shared_ptr<IFlexiblePointerTracker> TrackerSampleApp::TrackerHitTest(const PointerEvent & e)
+  {
+    // std::vector<boost::shared_ptr<MeasureTool>> measureTools_;
+    ScenePoint2D scenePos = e.GetMainPosition().Apply(
+      controller_->GetScene().GetCanvasToSceneTransform());
+
+    std::vector<boost::shared_ptr<MeasureTool> > measureTools = controller_->HitTestMeasureTools(scenePos);
+
+    if (measureTools.size() > 0)
+    {
+      return measureTools[0]->CreateEditionTracker(e);
+    }
+    return boost::shared_ptr<IFlexiblePointerTracker>();
+  }
+
+  static void GLAPIENTRY
+    OpenGLMessageCallback(GLenum source,
+      GLenum type,
+      GLuint id,
+      GLenum severity,
+      GLsizei length,
+      const GLchar* message,
+      const void* userParam)
+  {
+    if (severity != GL_DEBUG_SEVERITY_NOTIFICATION)
+    {
+      fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
+        (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""),
+        type, severity, message);
+    }
+  }
+
+  static bool g_stopApplication = false;
+  
+  ICompositor& TrackerSampleApp::GetCompositor()
+  {
+    using namespace Orthanc;
+    try
+    {
+      SdlViewport& viewport = dynamic_cast<SdlViewport&>(viewport_);
+      return viewport.GetCompositor();
+    }
+    catch (std::bad_cast e)
+    {
+      throw OrthancException(ErrorCode_InternalError, "Wrong viewport type!");
+     }
+  }
+
+  const ICompositor& TrackerSampleApp::GetCompositor() const
+  {
+    using namespace Orthanc;
+    try
+    {
+      SdlViewport& viewport = const_cast<SdlViewport&>(dynamic_cast<const SdlViewport&>(viewport_));
+      return viewport.GetCompositor();
+    }
+    catch (std::bad_cast e)
+    {
+      throw OrthancException(ErrorCode_InternalError, "Wrong viewport type!");
+    }
+  }
+
+
+  void TrackerSampleApp::Run()
+  {
+    controller_->FitContent(viewport_.GetCanvasWidth(), viewport_.GetCanvasHeight());
+
+    glEnable(GL_DEBUG_OUTPUT);
+    glDebugMessageCallback(OpenGLMessageCallback, 0);
+
+    GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT,
+      FONT_SIZE_0, Orthanc::Encoding_Latin1);
+    GetCompositor().SetFont(1, Orthanc::EmbeddedResources::UBUNTU_FONT,
+      FONT_SIZE_1, Orthanc::Encoding_Latin1);
+
+    while (!g_stopApplication)
+    {
+      GetCompositor().Refresh();
+
+      SDL_Event event;
+      while (!g_stopApplication && SDL_PollEvent(&event))
+      {
+        if (event.type == SDL_QUIT)
+        {
+          g_stopApplication = true;
+          break;
+        }
+        else if (event.type == SDL_WINDOWEVENT &&
+          event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
+        {
+          DisableTracker(); // was: tracker.reset(NULL);
+        }
+        else if (event.type == SDL_KEYDOWN &&
+          event.key.repeat == 0 /* Ignore key bounce */)
+        {
+          switch (event.key.keysym.sym)
+          {
+          case SDLK_f:
+            viewport_.GetWindow().ToggleMaximize();
+            break;
+
+          case SDLK_q:
+            g_stopApplication = true;
+            break;
+          default:
+            break;
+          }
+        }
+        HandleApplicationEvent(event);
+      }
+      SDL_Delay(1);
+    }
+  }
+
+  void TrackerSampleApp::SetInfoDisplayMessage(
+    std::string key, std::string value)
+  {
+    if (value == "")
+      infoTextMap_.erase(key);
+    else
+      infoTextMap_[key] = value;
+    DisplayInfoText();
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/Deprecated/Sdl/TrackerSampleApp.h	Tue Apr 07 14:31:28 2020 +0200
@@ -0,0 +1,148 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 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 "../../Framework/Messages/IObserver.h"
+#include "../../Framework/Scene2D/OpenGLCompositor.h"
+#include "../../Framework/Scene2DViewport/IFlexiblePointerTracker.h"
+#include "../../Framework/Scene2DViewport/MeasureTool.h"
+#include "../../Framework/Scene2DViewport/PredeclaredTypes.h"
+#include "../../Framework/Scene2DViewport/ViewportController.h"
+#include "../../Framework/Viewport/SdlViewport.h"
+
+#include <SDL.h>
+
+#include <boost/make_shared.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+namespace OrthancStone
+{
+  enum GuiTool
+  {
+    GuiTool_Rotate = 0,
+    GuiTool_Pan,
+    GuiTool_Zoom,
+    GuiTool_LineMeasure,
+    GuiTool_CircleMeasure,
+    GuiTool_AngleMeasure,
+    GuiTool_EllipseMeasure,
+    GuiTool_LAST
+  };
+
+  const char* MeasureToolToString(size_t i);
+
+  static const unsigned int FONT_SIZE_0 = 32;
+  static const unsigned int FONT_SIZE_1 = 24;
+
+  class Scene2D;
+  class UndoStack;
+
+  class TrackerSampleApp : public IObserver
+    , public boost::enable_shared_from_this<TrackerSampleApp>
+  {
+  public:
+    // 12 because.
+    TrackerSampleApp(MessageBroker& broker);
+    void PrepareScene();
+    void Run();
+    void SetInfoDisplayMessage(std::string key, std::string value);
+    void DisableTracker();
+
+    void HandleApplicationEvent(const SDL_Event& event);
+
+    /**
+    This method is called when the scene transform changes. It allows to
+    recompute the visual elements whose content depend upon the scene transform
+    */
+    void OnSceneTransformChanged(
+      const ViewportController::SceneTransformChanged& message);
+
+  private:
+    void SelectNextTool();
+    void CreateRandomMeasureTool();
+
+
+    /**
+    In the case of this app, the viewport is an SDL viewport and it has 
+    a OpenGLCompositor& GetCompositor() method
+    */
+    ICompositor& GetCompositor();
+
+    /**
+    See the other overload
+    */
+    const ICompositor& GetCompositor() const;
+
+    /**
+    This returns a random point in the canvas part of the scene, but in
+    scene coordinates
+    */
+    ScenePoint2D GetRandomPointInScene() const;
+
+    boost::shared_ptr<IFlexiblePointerTracker> TrackerHitTest(const PointerEvent& e);
+
+    boost::shared_ptr<IFlexiblePointerTracker> CreateSuitableTracker(
+      const SDL_Event& event,
+      const PointerEvent& e);
+
+    void TakeScreenshot(
+      const std::string& target,
+      unsigned int canvasWidth,
+      unsigned int canvasHeight);
+
+    /**
+      This adds the command at the top of the undo stack
+    */
+    void Commit(boost::shared_ptr<TrackerCommand> cmd);
+    void Undo();
+    void Redo();
+
+  private:
+    void DisplayFloatingCtrlInfoText(const PointerEvent& e);
+    void DisplayInfoText();
+    void HideInfoText();
+
+  private:
+    /**
+    WARNING: the measuring tools do store a reference to the scene, and it 
+    paramount that the scene gets destroyed AFTER the measurement tools.
+    */
+    boost::shared_ptr<ViewportController> controller_;
+
+    std::map<std::string, std::string> infoTextMap_;
+    boost::shared_ptr<IFlexiblePointerTracker> activeTracker_;
+
+    //static const int LAYER_POSITION = 150;
+
+    int TEXTURE_2x2_1_ZINDEX;
+    int TEXTURE_1x1_ZINDEX;
+    int TEXTURE_2x2_2_ZINDEX;
+    int LINESET_1_ZINDEX;
+    int LINESET_2_ZINDEX;
+    int FLOATING_INFOTEXT_LAYER_ZINDEX;
+    int FIXED_INFOTEXT_LAYER_ZINDEX;
+
+    GuiTool currentTool_;
+    boost::shared_ptr<UndoStack> undoStack_;
+    SdlOpenGLViewport viewport_;
+  };
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/Deprecated/Sdl/cpp.hint	Tue Apr 07 14:31:28 2020 +0200
@@ -0,0 +1,2 @@
+#define ORTHANC_OVERRIDE
+#define ORTHANC_STONE_DEFINE_EMPTY_MESSAGE(FILE, LINE, NAME) class NAME : public ::OrthancStone::IMessage {};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/Deprecated/WebAssembly/BasicMPR.cpp	Tue Apr 07 14:31:28 2020 +0200
@@ -0,0 +1,427 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 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 "dev.h"
+
+#include <emscripten.h>
+
+#include "../../Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h"
+#include "../../Framework/Oracle/SleepOracleCommand.h"
+#include "../../Framework/Oracle/WebAssemblyOracle.h"
+#include "../../Framework/Scene2D/GrayscaleStyleConfigurator.h"
+#include "../../Framework/StoneInitialization.h"
+#include "../../Framework/Volumes/VolumeSceneLayerSource.h"
+
+
+namespace OrthancStone
+{
+  class VolumeSlicerWidget : public IObserver
+  {
+  private:
+    OrthancStone::WebAssemblyViewport      viewport_;
+    std::unique_ptr<VolumeSceneLayerSource>  source_;
+    VolumeProjection                       projection_;
+    std::vector<CoordinateSystem3D>        planes_;
+    size_t                                 currentPlane_;
+
+    void Handle(const DicomVolumeImage::GeometryReadyMessage& message)
+    {
+      LOG(INFO) << "Geometry is available";
+
+      const VolumeImageGeometry& geometry = message.GetOrigin().GetGeometry();
+
+      const unsigned int depth = geometry.GetProjectionDepth(projection_);
+      currentPlane_ = depth / 2;
+      
+      planes_.resize(depth);
+
+      for (unsigned int z = 0; z < depth; z++)
+      {
+        planes_[z] = geometry.GetProjectionSlice(projection_, z);
+      }
+
+      Refresh();
+
+      viewport_.FitContent();
+    }
+    
+  public:
+    VolumeSlicerWidget(MessageBroker& broker,
+                         const std::string& canvas,
+                         VolumeProjection projection) :
+      IObserver(broker),
+      viewport_(broker, canvas),
+      projection_(projection),
+      currentPlane_(0)
+    {
+    }
+
+    void UpdateSize()
+    {
+      viewport_.UpdateSize();
+    }
+
+    void SetSlicer(int layerDepth,
+                   const boost::shared_ptr<IVolumeSlicer>& slicer,
+                   IObservable& loader,
+                   ILayerStyleConfigurator* configurator)
+    {
+      if (source_.get() != NULL)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls,
+                                        "Only one slicer can be registered");
+      }
+      
+      loader.RegisterObserverCallback(
+        new Callable<VolumeSlicerWidget, DicomVolumeImage::GeometryReadyMessage>
+        (*this, &VolumeSlicerWidget::Handle));
+
+      source_.reset(new VolumeSceneLayerSource(viewport_.GetScene(), layerDepth, slicer));
+
+      if (configurator != NULL)
+      {
+        source_->SetConfigurator(configurator);
+      }
+    }    
+
+    void Refresh()
+    {
+      if (source_.get() != NULL &&
+          currentPlane_ < planes_.size())
+      {
+        source_->Update(planes_[currentPlane_]);
+        viewport_.Refresh();
+      }
+    }
+
+    size_t GetSlicesCount() const
+    {
+      return planes_.size();
+    }
+
+    void Scroll(int delta)
+    {
+      if (!planes_.empty())
+      {
+        int tmp = static_cast<int>(currentPlane_) + delta;
+        unsigned int next;
+
+        if (tmp < 0)
+        {
+          next = 0;
+        }
+        else if (tmp >= static_cast<int>(planes_.size()))
+        {
+          next = planes_.size() - 1;
+        }
+        else
+        {
+          next = static_cast<size_t>(tmp);
+        }
+
+        if (next != currentPlane_)
+        {
+          currentPlane_ = next;
+          Refresh();
+        }
+      }
+    }
+  };
+}
+
+
+
+
+boost::shared_ptr<OrthancStone::DicomVolumeImage>  ct_(new OrthancStone::DicomVolumeImage);
+
+boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader>  loader_;
+
+std::unique_ptr<OrthancStone::VolumeSlicerWidget>  widget1_;
+std::unique_ptr<OrthancStone::VolumeSlicerWidget>  widget2_;
+std::unique_ptr<OrthancStone::VolumeSlicerWidget>  widget3_;
+
+OrthancStone::MessageBroker  broker_;
+OrthancStone::WebAssemblyOracle  oracle_(broker_);
+
+
+EM_BOOL OnWindowResize(int eventType, const EmscriptenUiEvent *uiEvent, void *userData)
+{
+  try
+  {
+    if (widget1_.get() != NULL)
+    {
+      widget1_->UpdateSize();
+    }
+  
+    if (widget2_.get() != NULL)
+    {
+      widget2_->UpdateSize();
+    }
+  
+    if (widget3_.get() != NULL)
+    {
+      widget3_->UpdateSize();
+    }
+  }
+  catch (Orthanc::OrthancException& e)
+  {
+    LOG(ERROR) << "Exception while updating canvas size: " << e.What();
+  }
+  
+  return true;
+}
+
+
+
+
+EM_BOOL OnAnimationFrame(double time, void *userData)
+{
+  try
+  {
+    if (widget1_.get() != NULL)
+    {
+      widget1_->Refresh();
+    }
+  
+    if (widget2_.get() != NULL)
+    {
+      widget2_->Refresh();
+    }
+  
+    if (widget3_.get() != NULL)
+    {
+      widget3_->Refresh();
+    }
+
+    return true;
+  }
+  catch (Orthanc::OrthancException& e)
+  {
+    LOG(ERROR) << "Exception in the animation loop, stopping now: " << e.What();
+    return false;
+  }  
+}
+
+
+static bool ctrlDown_ = false;
+
+
+EM_BOOL OnMouseWheel(int eventType,
+                     const EmscriptenWheelEvent *wheelEvent,
+                     void *userData)
+{
+  try
+  {
+    if (userData != NULL)
+    {
+      int delta = 0;
+
+      if (wheelEvent->deltaY < 0)
+      {
+        delta = -1;
+      }
+           
+      if (wheelEvent->deltaY > 0)
+      {
+        delta = 1;
+      }
+
+      OrthancStone::VolumeSlicerWidget& widget =
+        *reinterpret_cast<OrthancStone::VolumeSlicerWidget*>(userData);
+      
+      if (ctrlDown_)
+      {
+        delta *= static_cast<int>(widget.GetSlicesCount() / 10);
+      }
+
+      widget.Scroll(delta);
+    }
+  }
+  catch (Orthanc::OrthancException& e)
+  {
+    LOG(ERROR) << "Exception in the wheel event: " << e.What();
+  }
+  
+  return true;
+}
+
+
+EM_BOOL OnKeyDown(int eventType,
+                  const EmscriptenKeyboardEvent *keyEvent,
+                  void *userData)
+{
+  ctrlDown_ = keyEvent->ctrlKey;
+  return false;
+}
+
+
+EM_BOOL OnKeyUp(int eventType,
+                const EmscriptenKeyboardEvent *keyEvent,
+                void *userData)
+{
+  ctrlDown_ = false;
+  return false;
+}
+
+
+
+
+namespace OrthancStone
+{
+  class TestSleep : public IObserver
+  {
+  private:
+    WebAssemblyOracle&  oracle_;
+
+    void Schedule()
+    {
+      oracle_.Schedule(*this, new OrthancStone::SleepOracleCommand(2000));
+    }
+    
+    void Handle(const SleepOracleCommand::TimeoutMessage& message)
+    {
+      LOG(INFO) << "TIMEOUT";
+      Schedule();
+    }
+    
+  public:
+    TestSleep(MessageBroker& broker,
+              WebAssemblyOracle& oracle) :
+      IObserver(broker),
+      oracle_(oracle)
+    {
+      oracle.RegisterObserverCallback(
+        new Callable<TestSleep, SleepOracleCommand::TimeoutMessage>
+        (*this, &TestSleep::Handle));
+
+      LOG(INFO) << "STARTING";
+      Schedule();
+    }
+  };
+
+  //static TestSleep testSleep(broker_, oracle_);
+}
+
+
+
+static std::map<std::string, std::string> arguments_;
+
+static bool GetArgument(std::string& value,
+                        const std::string& key)
+{
+  std::map<std::string, std::string>::const_iterator found = arguments_.find(key);
+
+  if (found == arguments_.end())
+  {
+    return false;
+  }
+  else
+  {
+    value = found->second;
+    return true;
+  }
+}
+
+
+extern "C"
+{
+  int main(int argc, char const *argv[]) 
+  {
+    OrthancStone::StoneInitialize();
+    Orthanc::Logging::EnableInfoLevel(true);
+    // Orthanc::Logging::EnableTraceLevel(true);
+    EM_ASM(window.dispatchEvent(new CustomEvent("WebAssemblyLoaded")););
+  }
+
+  EMSCRIPTEN_KEEPALIVE
+  void SetArgument(const char* key, const char* value)
+  {
+    // This is called for each GET argument (cf. "app.js")
+    LOG(INFO) << "Received GET argument: [" << key << "] = [" << value << "]";
+    arguments_[key] = value;
+  }
+
+  EMSCRIPTEN_KEEPALIVE
+  void Initialize()
+  {
+    try
+    {
+      oracle_.SetOrthancRoot("..");
+      
+      loader_.reset(new OrthancStone::OrthancSeriesVolumeProgressiveLoader(ct_, oracle_, oracle_));
+    
+      widget1_.reset(new OrthancStone::VolumeSlicerWidget(broker_, "mycanvas1", OrthancStone::VolumeProjection_Axial));
+      {
+        std::unique_ptr<OrthancStone::GrayscaleStyleConfigurator> style(new OrthancStone::GrayscaleStyleConfigurator);
+        style->SetLinearInterpolation(true);
+        style->SetWindowing(OrthancStone::ImageWindowing_Bone);
+        widget1_->SetSlicer(0, loader_, *loader_, style.release());
+      }
+      widget1_->UpdateSize();
+
+      widget2_.reset(new OrthancStone::VolumeSlicerWidget(broker_, "mycanvas2", OrthancStone::VolumeProjection_Coronal));
+      {
+        std::unique_ptr<OrthancStone::GrayscaleStyleConfigurator> style(new OrthancStone::GrayscaleStyleConfigurator);
+        style->SetLinearInterpolation(true);
+        style->SetWindowing(OrthancStone::ImageWindowing_Bone);
+        widget2_->SetSlicer(0, loader_, *loader_, style.release());
+      }
+      widget2_->UpdateSize();
+
+      widget3_.reset(new OrthancStone::VolumeSlicerWidget(broker_, "mycanvas3", OrthancStone::VolumeProjection_Sagittal));
+      {
+        std::unique_ptr<OrthancStone::GrayscaleStyleConfigurator> style(new OrthancStone::GrayscaleStyleConfigurator);
+        style->SetLinearInterpolation(true);
+        style->SetWindowing(OrthancStone::ImageWindowing_Bone);
+        widget3_->SetSlicer(0, loader_, *loader_, style.release());
+      }
+      widget3_->UpdateSize();
+
+      emscripten_set_resize_callback("#window", NULL, false, OnWindowResize);
+
+      emscripten_set_wheel_callback("mycanvas1", widget1_.get(), false, OnMouseWheel);
+      emscripten_set_wheel_callback("mycanvas2", widget2_.get(), false, OnMouseWheel);
+      emscripten_set_wheel_callback("mycanvas3", widget3_.get(), false, OnMouseWheel);
+
+      emscripten_set_keydown_callback("#window", NULL, false, OnKeyDown);
+      emscripten_set_keyup_callback("#window", NULL, false, OnKeyUp);
+    
+      emscripten_request_animation_frame_loop(OnAnimationFrame, NULL);
+
+
+      std::string ct;
+      if (GetArgument(ct, "ct"))
+      {
+        //loader_->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa");
+        loader_->LoadSeries(ct);
+      }
+      else
+      {
+        LOG(ERROR) << "No Orthanc identifier for the CT series was provided";
+      }
+    }
+    catch (Orthanc::OrthancException& e)
+    {
+      LOG(ERROR) << "Exception during Initialize(): " << e.What();
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/Deprecated/WebAssembly/BasicMPR.html	Tue Apr 07 14:31:28 2020 +0200
@@ -0,0 +1,60 @@
+<!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" oncontextmenu="return false;"></canvas>
+    <canvas id="mycanvas2" oncontextmenu="return false;"></canvas>
+    <canvas id="mycanvas3" oncontextmenu="return false;"></canvas>
+
+    <script type="text/javascript" src="app.js"></script>
+    <script type="text/javascript" async src="BasicMPR.js"></script>
+  </body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/Deprecated/WebAssembly/BasicScene.cpp	Tue Apr 07 14:31:28 2020 +0200
@@ -0,0 +1,210 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 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 "dev.h"
+
+#include <emscripten.h>
+#include <emscripten/html5.h>
+
+// From Stone
+#include "../../Framework/Scene2D/ColorTextureSceneLayer.h"
+#include "../../Framework/StoneInitialization.h"
+
+// From Orthanc framework
+#include <Core/Images/Image.h>
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+
+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::unique_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::unique_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
+  if (1)
+  {
+    std::unique_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, 255, 0, 0);
+
+    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, 0, 255, 0);
+
+    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, 0, 0, 255);
+
+    scene.SetLayer(50, layer.release());
+  }
+
+  // Some text
+  if (1)
+  {
+    std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer);
+    layer->SetText("Hello");
+    scene.SetLayer(100, layer.release());
+  }
+}
+
+
+std::unique_ptr<OrthancStone::WebAssemblyViewport>  viewport1_;
+std::unique_ptr<OrthancStone::WebAssemblyViewport>  viewport2_;
+std::unique_ptr<OrthancStone::WebAssemblyViewport>  viewport3_;
+boost::shared_ptr<OrthancStone::ViewportController>   controller1_;
+boost::shared_ptr<OrthancStone::ViewportController>   controller2_;
+boost::shared_ptr<OrthancStone::ViewportController>   controller3_;
+OrthancStone::MessageBroker broker_;
+
+
+EM_BOOL OnWindowResize(
+  int eventType, const EmscriptenUiEvent *uiEvent, void *userData)
+{
+  if (viewport1_.get() != NULL)
+  {
+    viewport1_->UpdateSize();
+  }
+  
+  if (viewport2_.get() != NULL)
+  {
+    viewport2_->UpdateSize();
+  }
+  
+  if (viewport3_.get() != NULL)
+  {
+    viewport3_->UpdateSize();
+  }
+  
+  return true;
+}
+
+extern "C"
+{
+  int main(int argc, char const *argv[]) 
+  {
+    OrthancStone::StoneInitialize();
+    // Orthanc::Logging::EnableInfoLevel(true);
+    // Orthanc::Logging::EnableTraceLevel(true);
+    EM_ASM(window.dispatchEvent(new CustomEvent("WebAssemblyLoaded")););
+  }
+
+  EMSCRIPTEN_KEEPALIVE
+  void Initialize()
+  {
+    viewport1_.reset(new OrthancStone::WebAssemblyViewport("mycanvas1"));
+    PrepareScene(viewport1_->GetScene());
+    viewport1_->UpdateSize();
+
+    viewport2_.reset(new OrthancStone::WebAssemblyViewport("mycanvas2"));
+    PrepareScene(viewport2_->GetScene());
+    viewport2_->UpdateSize();
+
+    viewport3_.reset(new OrthancStone::WebAssemblyViewport("mycanvas3"));
+    PrepareScene(viewport3_->GetScene());
+    viewport3_->UpdateSize();
+
+    viewport1_->GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, 
+                                        FONT_SIZE, Orthanc::Encoding_Latin1);
+    viewport2_->GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, 
+                                        FONT_SIZE, Orthanc::Encoding_Latin1);
+    viewport3_->GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, 
+                                        FONT_SIZE, Orthanc::Encoding_Latin1);
+
+    controller1_.reset(new OrthancStone::ViewportController(boost::make_shared<OrthancStone::UndoStack>(), broker_, *viewport1_));
+    controller2_.reset(new OrthancStone::ViewportController(boost::make_shared<OrthancStone::UndoStack>(), broker_, *viewport2_));
+    controller3_.reset(new OrthancStone::ViewportController(boost::make_shared<OrthancStone::UndoStack>(), broker_, *viewport3_));
+
+    controller1_->FitContent(viewport1_->GetCanvasWidth(), viewport1_->GetCanvasHeight());
+    controller2_->FitContent(viewport2_->GetCanvasWidth(), viewport2_->GetCanvasHeight());
+    controller3_->FitContent(viewport3_->GetCanvasWidth(), viewport3_->GetCanvasHeight());
+
+    viewport1_->Refresh();
+    viewport2_->Refresh();
+    viewport3_->Refresh();
+
+    SetupEvents("mycanvas1", controller1_);
+    SetupEvents("mycanvas2", controller2_);
+    SetupEvents("mycanvas3", controller3_);
+
+    emscripten_set_resize_callback("#window", NULL, false, OnWindowResize);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/Deprecated/WebAssembly/BasicScene.html	Tue Apr 07 14:31:28 2020 +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" oncontextmenu="return false;"></canvas>
+    <canvas id="mycanvas2" oncontextmenu="return false;"></canvas>
+    <canvas id="mycanvas3" oncontextmenu="return false;"></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/Deprecated/WebAssembly/CMakeLists.txt	Tue Apr 07 14:31:28 2020 +0200
@@ -0,0 +1,119 @@
+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")
+#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXIT_RUNTIME=1")
+
+#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1")
+
+
+#####################################################################
+## 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)
+
+add_definitions(
+  -DORTHANC_ENABLE_LOGGING_PLUGIN=0
+  )
+
+
+#####################################################################
+## Build the samples
+#####################################################################
+
+add_library(OrthancStone STATIC
+  ${ORTHANC_STONE_SOURCES}
+  )
+
+
+if (ON)
+  add_executable(BasicScene
+    BasicScene.cpp
+    #${CMAKE_CURRENT_LIST_DIR}/../Shared/SharedBasicScene.h
+    ${CMAKE_CURRENT_LIST_DIR}/../Shared/SharedBasicScene.cpp
+    )
+
+  target_link_libraries(BasicScene OrthancStone)
+
+  install(
+    TARGETS BasicScene
+    RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}
+    )
+endif()
+
+
+if (ON)
+  add_executable(BasicMPR
+    BasicMPR.cpp
+    )
+
+  target_link_libraries(BasicMPR OrthancStone)
+
+  install(
+    TARGETS BasicMPR
+    RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}
+    )
+endif()
+  
+
+install(
+  FILES
+  ${CMAKE_CURRENT_BINARY_DIR}/BasicMPR.wasm
+  ${CMAKE_CURRENT_BINARY_DIR}/BasicScene.wasm
+  ${CMAKE_SOURCE_DIR}/BasicMPR.html
+  ${CMAKE_SOURCE_DIR}/BasicScene.html
+  ${CMAKE_SOURCE_DIR}/Configuration.json
+  ${CMAKE_SOURCE_DIR}/app.js
+  ${CMAKE_SOURCE_DIR}/index.html
+  DESTINATION ${CMAKE_INSTALL_PREFIX}
+  )
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/Deprecated/WebAssembly/Configuration.json	Tue Apr 07 14:31:28 2020 +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/Deprecated/WebAssembly/ConfigurationLocalSJO.json	Tue Apr 07 14:31:28 2020 +0200
@@ -0,0 +1,20 @@
+{
+  "Plugins": [
+    "/home/jodogne/Subversion/orthanc-webviewer/r/libOrthancWebViewer.so",
+    "/home/jodogne/Subversion/orthanc/r/libServeFolders.so"
+  ],
+  "StorageDirectory" : "/tmp/orthanc-db",
+  "IndexDirectory" : "/tmp/orthanc-db",
+  "RemoteAccessAllowed" : true,
+  "AuthenticationEnabled" : false,
+  "ServeFolders" : {
+    "AllowCache" : false,
+    "GenerateETag" : true,
+    "Folders" : {
+      "/stone" : "/tmp/stone"
+    }
+  },
+  "WebViewer" : {
+    "CachePath" : "/tmp/orthanc-db/WebViewerCache"
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/Deprecated/WebAssembly/NOTES.txt	Tue Apr 07 14:31:28 2020 +0200
@@ -0,0 +1,76 @@
+Docker SJO
+==========
+
+$ 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
+$ docker run -p 4242:4242 -p 8042:8042 --rm -v /tmp/stone:/root/stone:ro -v /tmp/stone-db/:/var/lib/orthanc/db/ jodogne/orthanc-plugins:latest /root/stone/Configuration.json --verbose
+
+WARNING: This won't work using "orthanc-plugins:1.5.6", as support for
+PAM is mandatatory in "/instances/.../image-uint16".
+
+
+Docker BGO
+==========
+
+On Ubuntu WSL
+-------------
+. ~/apps/emsdk/emsdk_env.sh
+cd /mnt/c/osi/dev/
+mkdir -p build_stone_newsamples_wasm_wsl
+mkdir -p build_install_stone_newsamples_wasm_wsl
+cd build_stone_newsamples_wasm_wsl
+cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON /mnt/c/osi/dev/orthanc-stone/Samples/WebAssembly -DCMAKE_INSTALL_PREFIX=/mnt/c/osi/dev/build_install_stone_newsamples_wasm_wsl
+ninja install
+
+Then, on Windows
+-----------------
+docker run -p 4242:4242 -p 8042:8042 --rm -v "C:/osi/dev/build_install_stone_newsamples_wasm_wsl:/root/stone:ro" jodogne/orthanc-plugins:1.5.6 /root/stone/Configuration.json --verbose
+
+# WAIT A COUPLE OF SECS
+# if the archive has NOT already been unzipped, unzip it
+# upload dicom files to running orthanc
+
+cd C:\osi\dev\twiga-orthanc-viewer\demo\dicomfiles
+if (-not (test-path RTVIEWER-c8febcc6-eb9e22a4-130f208c-e0a6a4cd-4d432c57)) { unzip RTVIEWER-c8febcc6-eb9e22a4-130f208c-e0a6a4cd-4d432c57.zip}
+ImportDicomFiles.ps1 127.0.0.1 8042 .\RTVIEWER-c8febcc6-eb9e22a4-130f208c-e0a6a4cd-4d432c57\
+
+--> localhost:8042 --> Plugins --> serve-folders --> stone --> ...
+
+Local BGO
+==========
+
+. ~/apps/emsdk/emsdk_env.sh
+cd /mnt/c/osi/dev/
+mkdir -p build_stone_newsamples_wasm_wsl
+mkdir -p build_install_stone_newsamples_wasm_wsl
+cd build_stone_newsamples_wasm_wsl
+cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON /mnt/c/osi/dev/orthanc-stone/Samples/WebAssembly -DCMAKE_INSTALL_PREFIX=/mnt/c/osi/dev/build_install_stone_newsamples_wasm_wsl
+
+
+
+TODO: Orthanc.exe 
+
+
+Local SJO
+==========
+
+$ 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
+
+$ make -C ~/Subversion/orthanc/r -j4
+$ make -C ~/Subversion/orthanc-webviewer/r -j4
+$ ~/Subversion/orthanc/r/Orthanc ../ConfigurationLocalSJO.json
+
+
+Local AM
+========
+
+. ~/apps/emsdk/emsdk_env.sh
+cd /mnt/c/o/
+mkdir -p build_stone_newsamples_wasm_wsl
+mkdir -p build_install_stone_newsamples_wasm_wsl
+cd build_stone_newsamples_wasm_wsl
+cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSDK}/fastcomp/emscripten/cmake/Modules/Platform/Emscripten.cmake -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT=/mnt/c/o/orthanc/ -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON /mnt/c/o/orthanc-stone/Samples/WebAssembly -DCMAKE_INSTALL_PREFIX=/mnt/c/o/build_install_stone_newsamples_wasm_wsl
+ninja
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/Deprecated/WebAssembly/app.js	Tue Apr 07 14:31:28 2020 +0200
@@ -0,0 +1,33 @@
+/**
+ * This is a generic bootstrap code that is shared by all the Stone
+ * sample applications.
+ **/
+
+// Check support for WebAssembly
+if (!('WebAssembly' in window)) {
+  alert('Sorry, your browser does not support WebAssembly :(');
+} else {
+
+  // Wait for the module to be loaded (the event "WebAssemblyLoaded"
+  // must be emitted by the "main" function)
+  window.addEventListener('WebAssemblyLoaded', function() {
+
+    // Loop over the GET arguments
+    var parameters = window.location.search.substr(1);
+    if (parameters != null && parameters != '') {
+      var tokens = parameters.split('&');
+      for (var i = 0; i < tokens.length; i++) {
+        var arg = tokens[i].split('=');
+        if (arg.length == 2) {
+
+          // Send each GET argument to WebAssembly
+          Module.ccall('SetArgument', null, [ 'string', 'string' ],
+                       [ arg[0], decodeURIComponent(arg[1]) ]);
+        }
+      }
+    }
+
+    // Inform the WebAssembly module that it can start
+    Module.ccall('Initialize', null, null, null);
+  });
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/Deprecated/WebAssembly/dev.h	Tue Apr 07 14:31:28 2020 +0200
@@ -0,0 +1,202 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2020 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
+
+#include "../../Framework/Viewport/WebAssemblyViewport.h"
+#include "../../Framework/Scene2D/OpenGLCompositor.h"
+#include "../../Framework/Scene2D/PanSceneTracker.h"
+#include "../../Framework/Scene2D/RotateSceneTracker.h"
+#include "../../Framework/Scene2D/ZoomSceneTracker.h"
+#include "../../Framework/Scene2DViewport/UndoStack.h"
+#include "../../Framework/Scene2DViewport/ViewportController.h"
+
+#include <Core/OrthancException.h>
+
+#include <emscripten/html5.h>
+#include <boost/make_shared.hpp>
+
+static const unsigned int FONT_SIZE = 32;
+
+namespace OrthancStone
+{
+  class ActiveTracker : public boost::noncopyable
+  {
+  private:
+    boost::shared_ptr<IFlexiblePointerTracker> tracker_;
+    std::string                             canvasIdentifier_;
+    bool                                    insideCanvas_;
+    
+  public:
+    ActiveTracker(const boost::shared_ptr<IFlexiblePointerTracker>& tracker,
+                  const std::string& canvasId) :
+      tracker_(tracker),
+      canvasIdentifier_(canvasId),
+      insideCanvas_(true)
+    {
+      if (tracker_.get() == NULL)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+      }
+    }
+
+    bool IsAlive() const
+    {
+      return tracker_->IsAlive();
+    }
+
+    void PointerMove(const PointerEvent& event)
+    {
+      tracker_->PointerMove(event);
+    }
+
+    void PointerUp(const PointerEvent& event)
+    {
+      tracker_->PointerUp(event);
+    }
+  };
+}
+
+static OrthancStone::PointerEvent* ConvertMouseEvent(
+  const EmscriptenMouseEvent&        source,
+  OrthancStone::IViewport& viewport)
+{
+  std::unique_ptr<OrthancStone::PointerEvent> target(
+    new OrthancStone::PointerEvent);
+
+  target->AddPosition(viewport.GetPixelCenterCoordinates(
+                        source.targetX, source.targetY));
+  target->SetAltModifier(source.altKey);
+  target->SetControlModifier(source.ctrlKey);
+  target->SetShiftModifier(source.shiftKey);
+
+  return target.release();
+}
+
+std::unique_ptr<OrthancStone::ActiveTracker> tracker_;
+
+EM_BOOL OnMouseEvent(int eventType, 
+                     const EmscriptenMouseEvent *mouseEvent, 
+                     void *userData)
+{
+  if (mouseEvent != NULL &&
+      userData != NULL)
+  {
+    boost::shared_ptr<OrthancStone::ViewportController>& controller = 
+      *reinterpret_cast<boost::shared_ptr<OrthancStone::ViewportController>*>(userData);
+
+    switch (eventType)
+    {
+      case EMSCRIPTEN_EVENT_CLICK:
+      {
+        static unsigned int count = 0;
+        char buf[64];
+        sprintf(buf, "click %d", count++);
+
+        std::unique_ptr<OrthancStone::TextSceneLayer> layer(new OrthancStone::TextSceneLayer);
+        layer->SetText(buf);
+        controller->GetViewport().GetScene().SetLayer(100, layer.release());
+        controller->GetViewport().Refresh();
+        break;
+      }
+
+      case EMSCRIPTEN_EVENT_MOUSEDOWN:
+      {
+        boost::shared_ptr<OrthancStone::IFlexiblePointerTracker> t;
+
+        {
+          std::unique_ptr<OrthancStone::PointerEvent> event(
+            ConvertMouseEvent(*mouseEvent, controller->GetViewport()));
+
+          switch (mouseEvent->button)
+          {
+            case 0:  // Left button
+              emscripten_console_log("Creating RotateSceneTracker");
+              t.reset(new OrthancStone::RotateSceneTracker(
+                        controller, *event));
+              break;
+
+            case 1:  // Middle button
+              emscripten_console_log("Creating PanSceneTracker");
+              LOG(INFO) << "Creating PanSceneTracker" ;
+              t.reset(new OrthancStone::PanSceneTracker(
+                        controller, *event));
+              break;
+
+            case 2:  // Right button
+              emscripten_console_log("Creating ZoomSceneTracker");
+              t.reset(new OrthancStone::ZoomSceneTracker(
+                        controller, *event, controller->GetViewport().GetCanvasWidth()));
+              break;
+
+            default:
+              break;
+          }
+        }
+
+        if (t.get() != NULL)
+        {
+          tracker_.reset(
+            new OrthancStone::ActiveTracker(t, controller->GetViewport().GetCanvasIdentifier()));
+          controller->GetViewport().Refresh();
+        }
+
+        break;
+      }
+
+      case EMSCRIPTEN_EVENT_MOUSEMOVE:
+        if (tracker_.get() != NULL)
+        {
+          std::unique_ptr<OrthancStone::PointerEvent> event(
+            ConvertMouseEvent(*mouseEvent, controller->GetViewport()));
+          tracker_->PointerMove(*event);
+          controller->GetViewport().Refresh();
+        }
+        break;
+
+      case EMSCRIPTEN_EVENT_MOUSEUP:
+        if (tracker_.get() != NULL)
+        {
+          std::unique_ptr<OrthancStone::PointerEvent> event(
+            ConvertMouseEvent(*mouseEvent, controller->GetViewport()));
+          tracker_->PointerUp(*event);
+          controller->GetViewport().Refresh();
+          if (!tracker_->IsAlive())
+            tracker_.reset();
+        }
+        break;
+
+      default:
+        break;
+    }
+  }
+
+  return true;
+}
+
+
+void SetupEvents(const std::string& canvas,
+                 boost::shared_ptr<OrthancStone::ViewportController>& controller)
+{
+  emscripten_set_mousedown_callback(canvas.c_str(), &controller, false, OnMouseEvent);
+  emscripten_set_mousemove_callback(canvas.c_str(), &controller, false, OnMouseEvent);
+  emscripten_set_mouseup_callback(canvas.c_str(), &controller, false, OnMouseEvent);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Samples/Deprecated/WebAssembly/index.html	Tue Apr 07 14:31:28 2020 +0200
@@ -0,0 +1,16 @@
+<!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>
+      <li><a href="BasicMPR.html">Basic MPR display</a></li>
+    </ul>
+  </body>
+</html>
--- a/Samples/MultiPlatform/BasicScene/BasicScene.cpp	Tue Apr 07 14:29:01 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,275 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 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 "BasicScene.h"
-
-// From Stone
-#include "Framework/Scene2D/Scene2D.h"
-#include "Framework/Scene2D/ColorTextureSceneLayer.h"
-#include "Framework/Scene2D/PolylineSceneLayer.h"
-#include "Framework/Scene2D/TextSceneLayer.h"
-
-#include "Framework/Scene2D/PanSceneTracker.h"
-#include "Framework/Scene2D/ZoomSceneTracker.h"
-#include "Framework/Scene2D/RotateSceneTracker.h"
-
-#include "Framework/Scene2D/CairoCompositor.h"
-
-// From Orthanc framework
-#include <Core/Images/Image.h>
-#include <Core/Images/ImageProcessing.h>
-#include <Core/Images/PngWriter.h>
-
-using namespace OrthancStone;
-
-const unsigned int BASIC_SCENE_FONT_SIZE = 32;
-const int BASIC_SCENE_LAYER_POSITION = 150;
-
-void PrepareScene(Scene2D& scene)
-{
-  //Scene2D& scene(*controller->GetScene());
-  // Texture of 2x2 size
-  {
-    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::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i));
-    l->SetOrigin(-3, 2);
-    l->SetPixelSpacing(1.5, 1);
-    l->SetAngle(20.0 / 180.0 * 3.14);
-    scene.SetLayer(14, l.release());
-  }
-
-  // Texture of 1x1 size
-  {
-    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::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i));
-    l->SetOrigin(-2, 1);
-    l->SetAngle(20.0 / 180.0 * 3.14);
-    scene.SetLayer(13, l.release());
-  }
-
-  // Some lines
-  {
-    std::unique_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, 255, 0, 0);
-
-    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, 0, 255, 0);
-
-    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, 0, 0, 255);
-
-    //    layer->SetColor(0,255, 255);
-    scene.SetLayer(50, layer.release());
-  }
-
-  // Some text
-  {
-    std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer);
-    layer->SetText("Hello");
-    scene.SetLayer(100, layer.release());
-  }
-}
-
-#if ORTHANC_SANDBOXED == 0
-void TakeScreenshot(const std::string& target,
-                    const OrthancStone::Scene2D& scene,
-                    unsigned int canvasWidth,
-                    unsigned int canvasHeight)
-{
-  using namespace OrthancStone;
-  // Take a screenshot, then save it as PNG file
-  CairoCompositor compositor(scene, canvasWidth, canvasHeight);
-  compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, BASIC_SCENE_FONT_SIZE, Orthanc::Encoding_Latin1);
-  compositor.Refresh();
-
-  Orthanc::ImageAccessor canvas;
-  compositor.GetCanvas().GetReadOnlyAccessor(canvas);
-
-  Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false);
-  Orthanc::ImageProcessing::Convert(png, canvas);
-
-  Orthanc::PngWriter writer;
-  writer.WriteToFile(target, png);
-}
-#endif
-
-void ShowCursorInfo(Scene2D& scene, const PointerEvent& pointerEvent)
-{
-  ScenePoint2D p = pointerEvent.GetMainPosition().Apply(scene.GetCanvasToSceneTransform());
-
-  char buf[64];
-  sprintf(buf, "(%0.02f,%0.02f)", p.GetX(), p.GetY());
-
-  if (scene.HasLayer(BASIC_SCENE_LAYER_POSITION))
-  {
-    TextSceneLayer& layer =
-        dynamic_cast<TextSceneLayer&>(scene.GetLayer(BASIC_SCENE_LAYER_POSITION));
-    layer.SetText(buf);
-    layer.SetPosition(p.GetX(), p.GetY());
-  }
-  else
-  {
-    std::unique_ptr<TextSceneLayer>
-        layer(new TextSceneLayer);
-    layer->SetColor(0, 255, 0);
-    layer->SetText(buf);
-    layer->SetBorder(20);
-    layer->SetAnchor(BitmapAnchor_BottomCenter);
-    layer->SetPosition(p.GetX(), p.GetY());
-    scene.SetLayer(BASIC_SCENE_LAYER_POSITION, layer.release());
-  }
-}
-
-
-
-bool BasicScene2DInteractor::OnMouseEvent(const GuiAdapterMouseEvent& event, const PointerEvent& pointerEvent)
-{
-  if (currentTracker_.get() != NULL)
-  {
-    switch (event.type)
-    {
-    case GUIADAPTER_EVENT_MOUSEUP:
-    {
-      currentTracker_->PointerUp(pointerEvent);
-      if (!currentTracker_->IsAlive())
-      {
-        currentTracker_.reset();
-      }
-    };break;
-    case GUIADAPTER_EVENT_MOUSEMOVE:
-    {
-      currentTracker_->PointerMove(pointerEvent);
-    };break;
-    default:
-      return false;
-    }
-    return true;
-  }
-  else if (event.type == GUIADAPTER_EVENT_MOUSEDOWN)
-  {
-    if (event.button == GUIADAPTER_MOUSEBUTTON_LEFT)
-    {
-      currentTracker_.reset(new RotateSceneTracker(viewportController_, pointerEvent));
-    }
-    else if (event.button == GUIADAPTER_MOUSEBUTTON_MIDDLE)
-    {
-      currentTracker_.reset(new PanSceneTracker(viewportController_, pointerEvent));
-    }
-    else if (event.button == GUIADAPTER_MOUSEBUTTON_RIGHT)
-    {
-      currentTracker_.reset(new ZoomSceneTracker(viewportController_, pointerEvent, viewportController_->GetViewport().GetCanvasHeight()));
-    }
-  }
-  else if (event.type == GUIADAPTER_EVENT_MOUSEMOVE)
-  {
-    if (showCursorInfo_)
-    {
-      Scene2D& scene(viewportController_->GetScene());
-      ShowCursorInfo(scene, pointerEvent);
-    }
-    return true;
-  }
-  return false;
-}
-
-bool BasicScene2DInteractor::OnKeyboardEvent(const GuiAdapterKeyboardEvent& guiEvent)
-{
-  if (guiEvent.type == GUIADAPTER_EVENT_KEYDOWN)
-  {
-    switch (guiEvent.sym[0])
-    {
-    case 's':
-    {
-      //viewportController_->FitContent(viewportController_->GetViewport().GetCanvasWidth(), viewportController_->GetViewport().GetCanvasHeight());
-      viewportController_->FitContent();
-      return true;
-    };
-#if ORTHANC_SANDBOXED == 0
-    case 'c':
-    {
-      Scene2D& scene(viewportController_->GetScene());
-      TakeScreenshot("screenshot.png", scene, viewportController_->GetViewport().GetCanvasWidth(), viewportController_->GetViewport().GetCanvasHeight());
-      return true;
-    }
-#endif
-    case 'd':
-    {
-      showCursorInfo_ = !showCursorInfo_;
-      if (!showCursorInfo_)
-      {
-        Scene2D& scene(viewportController_->GetScene());
-        scene.DeleteLayer(BASIC_SCENE_LAYER_POSITION);
-      }
-
-      return true;
-    }
-    }
-  }
-  return false;
-}
-
-bool BasicScene2DInteractor::OnWheelEvent(const GuiAdapterWheelEvent& guiEvent)
-{
-  return false;
-}
--- a/Samples/MultiPlatform/BasicScene/BasicScene.h	Tue Apr 07 14:29:01 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 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
-
-#include <boost/shared_ptr.hpp>
-#include "Framework/Scene2DViewport/ViewportController.h"
-#include "Framework/Scene2D/Scene2D.h"
-
-extern const unsigned int BASIC_SCENE_FONT_SIZE;
-extern const int BASIC_SCENE_LAYER_POSITION;
-
-extern void PrepareScene(OrthancStone::Scene2D& scene);
-extern void TakeScreenshot(const std::string& target,
-                           const OrthancStone::Scene2D& scene,
-                           unsigned int canvasWidth,
-                           unsigned int canvasHeight);
-
-
-#include "Applications/Generic/Scene2DInteractor.h"
-#include "Framework/Scene2DViewport/IFlexiblePointerTracker.h"
-
-
-class BasicScene2DInteractor : public OrthancStone::Scene2DInteractor
-{
-  boost::shared_ptr<OrthancStone::IFlexiblePointerTracker>  currentTracker_;
-  bool                                                      showCursorInfo_;
-public:
-  BasicScene2DInteractor(boost::shared_ptr<OrthancStone::ViewportController> viewportController) :
-    Scene2DInteractor(viewportController),
-    showCursorInfo_(false)
-  {}
-
-  virtual bool OnMouseEvent(const OrthancStone::GuiAdapterMouseEvent& event, const OrthancStone::PointerEvent& pointerEvent) override;
-  virtual bool OnKeyboardEvent(const OrthancStone::GuiAdapterKeyboardEvent& guiEvent) override;
-  virtual bool OnWheelEvent(const OrthancStone::GuiAdapterWheelEvent& guiEvent) override;
-};
-
--- a/Samples/MultiPlatform/BasicScene/mainQt.cpp	Tue Apr 07 14:29:01 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,103 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 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/>.
- **/
-
-#define GLEW_STATIC 1
-// From Stone
-#include "../../Framework/OpenGL/OpenGLIncludes.h"
-#include "../../Applications/Sdl/SdlWindow.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/Scene2DViewport/ViewportController.h"
-#include "../../Framework/Scene2DViewport/UndoStack.h"
-
-#include "../../Framework/StoneInitialization.h"
-#include "../../Framework/Messages/MessageBroker.h"
-
-// From Orthanc framework
-#include <Core/Logging.h>
-#include <Core/OrthancException.h>
-#include <Core/Images/Image.h>
-#include <Core/Images/ImageProcessing.h>
-#include <Core/Images/PngWriter.h>
-
-#include <boost/make_shared.hpp>
-#include <boost/ref.hpp>
-#include "EmbeddedResources.h"
-
-#include <stdio.h>
-#include <QDebug>
-#include <QWindow>
-
-#include "BasicScene.h"
-
-
-using namespace OrthancStone;
-
-
-
-static void GLAPIENTRY OpenGLMessageCallback(GLenum source,
-                                             GLenum type,
-                                             GLuint id,
-                                             GLenum severity,
-                                             GLsizei length,
-                                             const GLchar* message,
-                                             const void* userParam )
-{
-  if (severity != GL_DEBUG_SEVERITY_NOTIFICATION)
-  {
-    fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
-            ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ),
-            type, severity, message );
-  }
-}
-
-extern void InitGL();
-
-#include <QApplication>
-#include "BasicSceneWindow.h"
-
-int main(int argc, char* argv[])
-{
-  QApplication a(argc, argv);
-
-  OrthancStone::Samples::BasicSceneWindow window;
-  window.show();
-  window.GetOpenGlWidget().Init();
-
-  MessageBroker broker;
-  boost::shared_ptr<UndoStack> undoStack(new UndoStack);
-  boost::shared_ptr<ViewportController> controller = boost::make_shared<ViewportController>(undoStack, boost::ref(broker), window.GetOpenGlWidget());
-  PrepareScene(controller->GetScene());
-
-  window.GetOpenGlWidget().GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT,
-                     BASIC_SCENE_FONT_SIZE, Orthanc::Encoding_Latin1);
-
-  boost::shared_ptr<OrthancStone::Scene2DInteractor> interactor(new BasicScene2DInteractor(controller));
-  window.GetOpenGlWidget().SetInteractor(interactor);
-
-  controller->FitContent();
-
-  return a.exec();
-}
--- a/Samples/MultiPlatform/BasicScene/mainSdl.cpp	Tue Apr 07 14:29:01 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,199 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 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/>.
- **/
-
-
-// From Stone
-#include "Framework/Viewport/SdlViewport.h"
-#include "Framework/Scene2D/OpenGLCompositor.h"
-#include "Framework/Scene2DViewport/UndoStack.h"
-#include "Framework/StoneInitialization.h"
-#include "Framework/Messages/MessageBroker.h"
-
-// From Orthanc framework
-#include <Core/Logging.h>
-#include <Core/OrthancException.h>
-
-#include <boost/make_shared.hpp>
-#include <boost/ref.hpp>
-
-#include <SDL.h>
-#include <stdio.h>
-
-
-#include "BasicScene.h"
-
-using namespace OrthancStone;
-
-boost::shared_ptr<BasicScene2DInteractor> interactor;
-
-void HandleApplicationEvent(boost::shared_ptr<OrthancStone::ViewportController> controller,
-                            const SDL_Event& event)
-{
-  using namespace OrthancStone;
-  Scene2D& scene(controller->GetScene());
-  if (event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_MOUSEBUTTONUP || event.type == SDL_MOUSEMOTION)
-  {
-    // TODO: this code is copy/pasted from GuiAdapter::Run() -> find the right place
-    int scancodeCount = 0;
-    const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount);
-    bool ctrlPressed(false);
-    bool shiftPressed(false);
-    bool altPressed(false);
-
-    if (SDL_SCANCODE_LCTRL < scancodeCount && keyboardState[SDL_SCANCODE_LCTRL])
-      ctrlPressed = true;
-    if (SDL_SCANCODE_RCTRL < scancodeCount && keyboardState[SDL_SCANCODE_RCTRL])
-      ctrlPressed = true;
-    if (SDL_SCANCODE_LSHIFT < scancodeCount && keyboardState[SDL_SCANCODE_LSHIFT])
-      shiftPressed = true;
-    if (SDL_SCANCODE_RSHIFT < scancodeCount && keyboardState[SDL_SCANCODE_RSHIFT])
-      shiftPressed = true;
-    if (SDL_SCANCODE_LALT < scancodeCount && keyboardState[SDL_SCANCODE_LALT])
-      altPressed = true;
-
-    GuiAdapterMouseEvent guiEvent;
-    ConvertFromPlatform(guiEvent, ctrlPressed, shiftPressed, altPressed, event);
-    PointerEvent pointerEvent;
-    pointerEvent.AddPosition(controller->GetViewport().GetPixelCenterCoordinates(event.button.x, event.button.y));
-
-    interactor->OnMouseEvent(guiEvent, pointerEvent);
-    return;
-  }
-  else if ((event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) && event.key.repeat == 0  /* Ignore key bounce */)
-  {
-    GuiAdapterKeyboardEvent guiEvent;
-    ConvertFromPlatform(guiEvent, event);
-
-    interactor->OnKeyboardEvent(guiEvent);
-  }
-
-}
-
-
-static void GLAPIENTRY
-OpenGLMessageCallback(GLenum source,
-                      GLenum type,
-                      GLuint id,
-                      GLenum severity,
-                      GLsizei length,
-                      const GLchar* message,
-                      const void* userParam )
-{
-  if (severity != GL_DEBUG_SEVERITY_NOTIFICATION)
-  {
-    fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
-            ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ),
-            type, severity, message );
-  }
-}
-
-
-void Run(boost::shared_ptr<OrthancStone::ViewportController> controller)
-{
-  SdlViewport& sdlViewport = dynamic_cast<SdlViewport&>(controller->GetViewport());
-
-  glEnable(GL_DEBUG_OUTPUT);
-  glDebugMessageCallback(OpenGLMessageCallback, 0);
-
-  controller->GetViewport().GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT,
-                     BASIC_SCENE_FONT_SIZE, Orthanc::Encoding_Latin1);
-
-  controller->GetViewport().Refresh();
-  controller->FitContent();
-
-
-  bool stop = false;
-  while (!stop)
-  {
-    controller->GetViewport().Refresh();
-
-    SDL_Event event;
-    while (!stop &&
-           SDL_PollEvent(&event))
-    {
-      if (event.type == SDL_QUIT)
-      {
-        stop = true;
-        break;
-      }
-      else if (event.type == SDL_WINDOWEVENT &&
-               event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
-      {
-        sdlViewport.UpdateSize(event.window.data1, event.window.data2);
-      }
-      else if (event.type == SDL_KEYDOWN &&
-               event.key.repeat == 0 /* Ignore key bounce */)
-      {
-        switch (event.key.keysym.sym)
-        {
-          case SDLK_f:
-            sdlViewport.GetWindow().ToggleMaximize();
-            break;
-              
-          case SDLK_q:
-            stop = true;
-            break;
-
-          default:
-            break;
-        }
-      }
-      
-      HandleApplicationEvent(controller, event);
-    }
-
-    SDL_Delay(1);
-  }
-  interactor.reset();
-}
-
-
-
-
-/**
- * IMPORTANT: The full arguments to "main()" are needed for SDL on
- * Windows. Otherwise, one gets the linking error "undefined reference
- * to `SDL_main'". https://wiki.libsdl.org/FAQWindows
- **/
-int main(int argc, char* argv[])
-{
-  using namespace OrthancStone;
-  StoneInitialize();
-  Orthanc::Logging::EnableInfoLevel(true);
-
-  try
-  {
-    SdlOpenGLViewport viewport("Hello", 1024, 768);
-    MessageBroker broker;
-    boost::shared_ptr<UndoStack> undoStack(new UndoStack);
-    boost::shared_ptr<ViewportController> controller = boost::make_shared<ViewportController>(undoStack, boost::ref(broker), boost::ref(viewport));
-    interactor.reset(new BasicScene2DInteractor(controller));
-    PrepareScene(controller->GetScene());
-    Run(controller);
-  }
-  catch (Orthanc::OrthancException& e)
-  {
-    LOG(ERROR) << "EXCEPTION: " << e.What();
-  }
-
-  StoneFinalize();
-
-  return 0;
-}
--- a/Samples/Qt/BasicSceneWindow.cpp	Tue Apr 07 14:29:01 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 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 "../../Framework/OpenGL/OpenGLIncludes.h"
-#include "BasicSceneWindow.h"
-
-/**
- * Don't use "ui_MainWindow.h" instead of <ui_MainWindow.h> below, as
- * this makes CMake unable to detect when the UI file changes.
- **/
-#include <ui_BasicSceneWindow.h>
-#include "../../Applications/Samples/SampleApplicationBase.h"
-
-namespace OrthancStone
-{
-  namespace Samples
-  {
-
-    BasicSceneWindow::BasicSceneWindow(
-      QWidget *parent) :
-      ui_(new Ui::BasicSceneWindow)
-    {
-      ui_->setupUi(this);
-    }
-
-    BasicSceneWindow::~BasicSceneWindow()
-    {
-      delete ui_;
-    }
-
-    QStoneOpenGlWidget& BasicSceneWindow::GetOpenGlWidget()
-    {
-      return *(ui_->centralWidget);
-    }
-
-  }
-}
--- a/Samples/Qt/BasicSceneWindow.h	Tue Apr 07 14:29:01 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 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
-#include <QMainWindow>
-#include <QStoneOpenGlWidget.h>
-// #include "../../Qt/QCairoWidget.h"
-// #include "../../Qt/QStoneMainWindow.h"
-
-namespace Ui 
-{
-  class BasicSceneWindow;
-}
-
-namespace OrthancStone
-{
-  namespace Samples
-  {
-
-    //class SampleSingleCanvasApplicationBase;
-
-    class BasicSceneWindow : public QMainWindow
-    {
-      Q_OBJECT
-
-    private:
-      Ui::BasicSceneWindow*   ui_;
-      //SampleSingleCanvasApplicationBase&  stoneSampleApplication_;
-
-    public:
-      explicit BasicSceneWindow(QWidget *parent = 0);
-      ~BasicSceneWindow();
-
-      QStoneOpenGlWidget& GetOpenGlWidget();
-    };
-  }
-}
--- a/Samples/Qt/BasicSceneWindow.ui	Tue Apr 07 14:29:01 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,84 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>BasicSceneWindow</class>
- <widget class="QMainWindow" name="BasicSceneWindow">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>903</width>
-    <height>634</height>
-   </rect>
-  </property>
-  <property name="minimumSize">
-   <size>
-    <width>500</width>
-    <height>300</height>
-   </size>
-  </property>
-  <property name="baseSize">
-   <size>
-    <width>500</width>
-    <height>300</height>
-   </size>
-  </property>
-  <property name="windowTitle">
-   <string>Stone of Orthanc</string>
-  </property>
-  <property name="layoutDirection">
-   <enum>Qt::LeftToRight</enum>
-  </property>
-  <widget class="QWidget" name="mainWidget">
-   <property name="sizePolicy">
-    <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
-     <horstretch>0</horstretch>
-     <verstretch>0</verstretch>
-    </sizepolicy>
-   </property>
-   <property name="layoutDirection">
-    <enum>Qt::LeftToRight</enum>
-   </property>
-   <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0">
-    <property name="sizeConstraint">
-     <enum>QLayout::SetDefaultConstraint</enum>
-    </property>
-    <item>
-     <widget class="OrthancStone::QStoneOpenGlWidget" name="centralWidget" native="true">
-      <property name="minimumSize">
-       <size>
-        <width>0</width>
-        <height>500</height>
-       </size>
-      </property>
-     </widget>
-    </item>
-   </layout>
-  </widget>
-  <widget class="QMenuBar" name="menubar">
-   <property name="geometry">
-    <rect>
-     <x>0</x>
-     <y>0</y>
-     <width>903</width>
-     <height>21</height>
-    </rect>
-   </property>
-   <widget class="QMenu" name="menuTest">
-    <property name="title">
-     <string>Test</string>
-    </property>
-   </widget>
-   <addaction name="menuTest"/>
-  </widget>
-  <widget class="QStatusBar" name="statusbar"/>
- </widget>
- <customwidgets>
-  <customwidget>
-   <class>QStoneOpenGlWidget</class>
-   <extends>QWidget</extends>
-   <header location="global">QStoneOpenGlWidget.h</header>
-  </customwidget>
- </customwidgets>
- <resources/>
- <connections/>
-</ui>
--- a/Samples/Qt/CMakeLists.txt	Tue Apr 07 14:29:01 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,83 +0,0 @@
-cmake_minimum_required(VERSION 2.8.3)
-
-#####################################################################
-## 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(ENABLE_QT ON)
-SET(ENABLE_SDL OFF)
-SET(ENABLE_WEB_CLIENT ON)
-SET(ORTHANC_SANDBOXED OFF)
-LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options)
-
-include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneConfiguration.cmake)
-
-add_definitions(
-  -DORTHANC_ENABLE_LOGGING_PLUGIN=0
-  )
-#####################################################################
-## Build the samples
-#####################################################################
-
-add_library(OrthancStone STATIC
-  ${ORTHANC_STONE_SOURCES}
-  )
-
-list(APPEND BASIC_SCENE_APPLICATIONS_SOURCES
-  BasicSceneWindow.cpp
-  )
-
-ORTHANC_QT_WRAP_UI(BASIC_SCENE_APPLICATIONS_SOURCES
-  BasicSceneWindow.ui
-  )
-
-ORTHANC_QT_WRAP_CPP(BASIC_SCENE_APPLICATIONS_SOURCES
-  BasicSceneWindow.h
-  QStoneOpenGlWidget.h
-  )
-
-add_executable(MpBasicScene
-  ${CMAKE_CURRENT_LIST_DIR}/../MultiPlatform/BasicScene/BasicScene.h
-  ${CMAKE_CURRENT_LIST_DIR}/../MultiPlatform/BasicScene/BasicScene.cpp
-  ${CMAKE_CURRENT_LIST_DIR}/../MultiPlatform/BasicScene/mainQt.cpp
-  QStoneOpenGlWidget.cpp
-  ${BASIC_SCENE_APPLICATIONS_SOURCES}
-  )
-
-target_include_directories(MpBasicScene PUBLIC ${CMAKE_SOURCE_DIR} ${ORTHANC_STONE_ROOT})
-target_link_libraries(MpBasicScene OrthancStone)
--- a/Samples/Qt/QStoneOpenGlWidget.cpp	Tue Apr 07 14:29:01 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,171 +0,0 @@
-#include "../../Framework/OpenGL/OpenGLIncludes.h"
-#include "QStoneOpenGlWidget.h"
-
-#include <QMouseEvent>
-
-using namespace OrthancStone;
-
-void QStoneOpenGlWidget::initializeGL()
-{
-  glewInit();
-}
-
-void QStoneOpenGlWidget::MakeCurrent()
-{
-  this->makeCurrent();
-}
-
-void QStoneOpenGlWidget::resizeGL(int w, int h)
-{
-
-}
-
-void QStoneOpenGlWidget::paintGL()
-{
-  if (compositor_)
-  {
-    compositor_->Refresh();
-  }
-  doneCurrent();
-}
-
-void ConvertFromPlatform(
-    OrthancStone::GuiAdapterMouseEvent& guiEvent,
-    PointerEvent& pointerEvent,
-    const QMouseEvent& qtEvent,
-    const IViewport& viewport)
-{
-  guiEvent.targetX = qtEvent.x();
-  guiEvent.targetY = qtEvent.y();
-  pointerEvent.AddPosition(viewport.GetPixelCenterCoordinates(guiEvent.targetX, guiEvent.targetY));
-
-  switch (qtEvent.button())
-  {
-  case Qt::LeftButton: guiEvent.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_LEFT; break;
-  case Qt::MiddleButton: guiEvent.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_MIDDLE; break;
-  case Qt::RightButton: guiEvent.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_RIGHT; break;
-  default:
-    guiEvent.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_LEFT;
-  }
-
-  if (qtEvent.modifiers().testFlag(Qt::ShiftModifier))
-  {
-    guiEvent.shiftKey = true;
-  }
-  if (qtEvent.modifiers().testFlag(Qt::ControlModifier))
-  {
-    guiEvent.ctrlKey = true;
-  }
-  if (qtEvent.modifiers().testFlag(Qt::AltModifier))
-  {
-    guiEvent.altKey = true;
-  }
-}
-
-void QStoneOpenGlWidget::mouseEvent(QMouseEvent* qtEvent, OrthancStone::GuiAdapterHidEventType guiEventType)
-{
-  OrthancStone::GuiAdapterMouseEvent guiEvent;
-  PointerEvent pointerEvent;
-  ConvertFromPlatform(guiEvent, pointerEvent, *qtEvent, *this);
-  guiEvent.type = guiEventType;
-
-  if (sceneInteractor_.get() != NULL && compositor_.get() != NULL)
-  {
-    sceneInteractor_->OnMouseEvent(guiEvent, pointerEvent);
-  }
-
-  // force redraw of the OpenGL widget
-  update();
-}
-
-void QStoneOpenGlWidget::mousePressEvent(QMouseEvent* qtEvent)
-{
-  mouseEvent(qtEvent, GUIADAPTER_EVENT_MOUSEDOWN);
-}
-
-void QStoneOpenGlWidget::mouseMoveEvent(QMouseEvent* qtEvent)
-{
-  mouseEvent(qtEvent, GUIADAPTER_EVENT_MOUSEMOVE);
-}
-
-void QStoneOpenGlWidget::mouseReleaseEvent(QMouseEvent* qtEvent)
-{
-  mouseEvent(qtEvent, GUIADAPTER_EVENT_MOUSEUP);
-}
-
-void ConvertFromPlatform(
-    OrthancStone::GuiAdapterKeyboardEvent& guiEvent,
-    const QKeyEvent& qtEvent)
-{
-  if (qtEvent.text().length() > 0)
-  {
-    guiEvent.sym[0] = qtEvent.text()[0].cell();
-  }
-  else
-  {
-    guiEvent.sym[0] = 0;
-  }
-  guiEvent.sym[1] = 0;
-
-  if (qtEvent.modifiers().testFlag(Qt::ShiftModifier))
-  {
-    guiEvent.shiftKey = true;
-  }
-  if (qtEvent.modifiers().testFlag(Qt::ControlModifier))
-  {
-    guiEvent.ctrlKey = true;
-  }
-  if (qtEvent.modifiers().testFlag(Qt::AltModifier))
-  {
-    guiEvent.altKey = true;
-  }
-
-}
-
-
-bool QStoneOpenGlWidget::keyEvent(QKeyEvent* qtEvent, OrthancStone::GuiAdapterHidEventType guiEventType)
-{
-  bool handled = false;
-  OrthancStone::GuiAdapterKeyboardEvent guiEvent;
-  ConvertFromPlatform(guiEvent, *qtEvent);
-  guiEvent.type = guiEventType;
-
-  if (sceneInteractor_.get() != NULL && compositor_.get() != NULL)
-  {
-    handled = sceneInteractor_->OnKeyboardEvent(guiEvent);
-
-    if (handled)
-    {
-      // force redraw of the OpenGL widget
-      update();
-    }
-  }
-  return handled;
-}
-
-void QStoneOpenGlWidget::keyPressEvent(QKeyEvent *qtEvent)
-{
-  bool handled = keyEvent(qtEvent, GUIADAPTER_EVENT_KEYDOWN);
-  if (!handled)
-  {
-    QOpenGLWidget::keyPressEvent(qtEvent);
-  }
-}
-
-void QStoneOpenGlWidget::keyReleaseEvent(QKeyEvent *qtEvent)
-{
-  bool handled = keyEvent(qtEvent, GUIADAPTER_EVENT_KEYUP);
-  if (!handled)
-  {
-    QOpenGLWidget::keyPressEvent(qtEvent);
-  }
-}
-
-void QStoneOpenGlWidget::wheelEvent(QWheelEvent *qtEvent)
-{
-  OrthancStone::GuiAdapterWheelEvent guiEvent;
-  throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
-
-  // force redraw of the OpenGL widget
-  update();
-}
--- a/Samples/Qt/QStoneOpenGlWidget.h	Tue Apr 07 14:29:01 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,90 +0,0 @@
-#pragma once
-#include "../../Framework/OpenGL/OpenGLIncludes.h"
-#include <QOpenGLWidget>
-#include <QOpenGLFunctions>
-#include <QOpenGLContext>
-
-#include <boost/shared_ptr.hpp>
-#include "../../Framework/OpenGL/IOpenGLContext.h"
-#include "../../Framework/Scene2D/OpenGLCompositor.h"
-#include "../../Framework/Viewport/ViewportBase.h"
-#include "../../Applications/Generic/Scene2DInteractor.h"
-
-namespace OrthancStone
-{
-  class QStoneOpenGlWidget :
-      public QOpenGLWidget,
-      public OpenGL::IOpenGLContext,
-      public ViewportBase
-  {
-    std::unique_ptr<OrthancStone::OpenGLCompositor> compositor_;
-    boost::shared_ptr<Scene2DInteractor> sceneInteractor_;
-    QOpenGLContext                        openGlContext_;
-
-  public:
-    QStoneOpenGlWidget(QWidget *parent) :
-      QOpenGLWidget(parent),
-      ViewportBase("QtStoneOpenGlWidget")  // TODO: we shall be able to define a name but construction time is too early !
-    {
-      setFocusPolicy(Qt::StrongFocus);  // to enable keyPressEvent
-      setMouseTracking(true);           // to enable mouseMoveEvent event when no button is pressed
-    }
-
-    void Init()
-    {
-      QSurfaceFormat requestedFormat;
-      requestedFormat.setVersion( 2, 0 );
-      openGlContext_.setFormat( requestedFormat );
-      openGlContext_.create();
-      openGlContext_.makeCurrent(context()->surface());
-
-      compositor_.reset(new OpenGLCompositor(*this, GetScene()));
-    }
-
-  protected:
-
-    //**** QWidget overrides
-    void initializeGL() override;
-    void resizeGL(int w, int h) override;
-    void paintGL() override;
-
-    void mousePressEvent(QMouseEvent* event) override;
-    void mouseMoveEvent(QMouseEvent* event) override;
-    void mouseReleaseEvent(QMouseEvent* event) override;
-    void keyPressEvent(QKeyEvent* event) override;
-    void keyReleaseEvent(QKeyEvent *event) override;
-    void wheelEvent(QWheelEvent* event) override;
-
-    //**** IOpenGLContext overrides
-
-    virtual void MakeCurrent() override;
-    virtual void SwapBuffer() override {}
-
-    virtual unsigned int GetCanvasWidth() const override
-    {
-      return this->width();
-    }
-
-    virtual unsigned int GetCanvasHeight() const override
-    {
-      return this->height();
-    }
-
-  public:
-
-    void SetInteractor(boost::shared_ptr<Scene2DInteractor> sceneInteractor)
-    {
-      sceneInteractor_ = sceneInteractor;
-    }
-
-    virtual ICompositor& GetCompositor()
-    {
-      return *compositor_;
-    }
-
-  protected:
-    void mouseEvent(QMouseEvent* qtEvent, OrthancStone::GuiAdapterHidEventType guiEventType);
-    bool keyEvent(QKeyEvent* qtEvent, OrthancStone::GuiAdapterHidEventType guiEventType);
-
-  };
-}
--- a/Samples/Qt/Scene2DInteractor.cpp	Tue Apr 07 14:29:01 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,72 +0,0 @@
-#include "Scene2DInteractor.h"
-
-#include "../../Framework/Scene2D/PanSceneTracker.h"
-#include "../../Framework/Scene2D/ZoomSceneTracker.h"
-#include "../../Framework/Scene2D/RotateSceneTracker.h"
-
-
-namespace OrthancStone
-{
-
-}
-
-using namespace OrthancStone;
-
-
-bool BasicScene2DInteractor::OnMouseEvent(const GuiAdapterMouseEvent& event, const PointerEvent& pointerEvent)
-{
-  if (currentTracker_.get() != NULL)
-  {
-    switch (event.type)
-    {
-    case GUIADAPTER_EVENT_MOUSEUP:
-    {
-      currentTracker_->PointerUp(pointerEvent);
-      if (!currentTracker_->IsAlive())
-      {
-        currentTracker_.reset();
-      }
-    };break;
-    case GUIADAPTER_EVENT_MOUSEMOVE:
-    {
-      currentTracker_->PointerMove(pointerEvent);
-    };break;
-    }
-    return true;
-  }
-  else
-  {
-    if (event.button == GUIADAPTER_MOUSEBUTTON_LEFT)
-    {
-      currentTracker_.reset(new RotateSceneTracker(viewportController_, pointerEvent));
-    }
-    else if (event.button == GUIADAPTER_MOUSEBUTTON_MIDDLE)
-    {
-      currentTracker_.reset(new PanSceneTracker(viewportController_, pointerEvent));
-    }
-    else if (event.button == GUIADAPTER_MOUSEBUTTON_RIGHT && compositor_.get() != NULL)
-    {
-      currentTracker_.reset(new ZoomSceneTracker(viewportController_, pointerEvent, compositor_->GetHeight()));
-    }
-    return true;
-  }
-  return false;
-}
-
-bool BasicScene2DInteractor::OnKeyboardEvent(const GuiAdapterKeyboardEvent& guiEvent)
-{
-  switch (guiEvent.sym[0])
-  {
-  case 's':
-  {
-    viewportController_->FitContent(compositor_->GetWidth(), compositor_->GetHeight());
-    return true;
-  };
-  }
-  return false;
-}
-
-bool BasicScene2DInteractor::OnWheelEvent(const GuiAdapterWheelEvent& guiEvent)
-{
-  return false;
-}
--- a/Samples/Qt/Scene2DInteractor.h	Tue Apr 07 14:29:01 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,19 +0,0 @@
-#pragma once
-
-#include "../../Applications/Generic/Scene2DInteractor.h"
-#include "../../Framework/Scene2DViewport/IFlexiblePointerTracker.h"
-
-
-class BasicScene2DInteractor : public OrthancStone::Scene2DInteractor
-{
-  boost::shared_ptr<OrthancStone::IFlexiblePointerTracker>  currentTracker_;
-public:
-  BasicScene2DInteractor(boost::shared_ptr<OrthancStone::ViewportController> viewportController) :
-    Scene2DInteractor(viewportController)
-  {}
-
-  virtual bool OnMouseEvent(const OrthancStone::GuiAdapterMouseEvent& event, const OrthancStone::PointerEvent& pointerEvent) override;
-  virtual bool OnKeyboardEvent(const OrthancStone::GuiAdapterKeyboardEvent& guiEvent);
-  virtual bool OnWheelEvent(const OrthancStone::GuiAdapterWheelEvent& guiEvent);
-};
-
--- a/Samples/Sdl/BasicScene.cpp	Tue Apr 07 14:29:01 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,418 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 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/>.
- **/
-
-
-// From Stone
-#include "../../Framework/Viewport/SdlViewport.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/ZoomSceneTracker.h"
-#include "../../Framework/Scene2DViewport/ViewportController.h"
-#include "../../Framework/Scene2DViewport/UndoStack.h"
-
-#include "../../Framework/StoneInitialization.h"
-#include "../../Framework/Messages/MessageBroker.h"
-
-// From Orthanc framework
-#include <Core/Logging.h>
-#include <Core/OrthancException.h>
-#include <Core/Images/Image.h>
-#include <Core/Images/ImageProcessing.h>
-#include <Core/Images/PngWriter.h>
-
-#include <boost/make_shared.hpp>
-
-#include <SDL.h>
-#include <stdio.h>
-
-static const unsigned int FONT_SIZE = 32;
-static const int LAYER_POSITION = 150;
-
-#define OPENGL_ENABLED 0
-
-void PrepareScene(OrthancStone::Scene2D& scene)
-{
-  using namespace OrthancStone;
-
-  // Texture of 2x2 size
-  {
-    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::unique_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
-  {
-    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::unique_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::unique_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer);
-
-    layer->SetThickness(10);
-
-    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, 255, 0, 0);
-
-    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, 0, 255, 0);
-
-    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, 0, 0, 255);
-
-    scene.SetLayer(50, layer.release());
-  }
-
-  // Some text
-  {
-    std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer);
-    layer->SetText("Hello");
-    scene.SetLayer(100, layer.release());
-  }
-}
-
-
-void TakeScreenshot(const std::string& target,
-                    const OrthancStone::Scene2D& scene,
-                    unsigned int canvasWidth,
-                    unsigned int canvasHeight)
-{
-  using namespace OrthancStone;
-  // Take a screenshot, then save it as PNG file
-  CairoCompositor compositor(scene, canvasWidth, canvasHeight);
-  compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, FONT_SIZE, Orthanc::Encoding_Latin1);
-  compositor.Refresh();
-
-  Orthanc::ImageAccessor canvas;
-  compositor.GetCanvas().GetReadOnlyAccessor(canvas);
-
-  Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false);
-  Orthanc::ImageProcessing::Convert(png, canvas);
-        
-  Orthanc::PngWriter writer;
-  writer.WriteToFile(target, png);
-}
-
-
-void HandleApplicationEvent(const SDL_Event& event,
-                            boost::shared_ptr<OrthancStone::ViewportController>& controller,
-                            boost::shared_ptr<OrthancStone::IFlexiblePointerTracker>& activeTracker)
-{
-  using namespace OrthancStone;
-
-  Scene2D& scene = controller->GetScene();
-  IViewport& viewport = controller->GetViewport();
-
-  if (event.type == SDL_MOUSEMOTION)
-  {
-    int scancodeCount = 0;
-    const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount);
-
-    if (activeTracker.get() == NULL &&
-        SDL_SCANCODE_LCTRL < scancodeCount &&
-        keyboardState[SDL_SCANCODE_LCTRL])
-    {
-      // The "left-ctrl" key is down, while no tracker is present
-
-      PointerEvent e;
-      e.AddPosition(viewport.GetPixelCenterCoordinates(event.button.x, event.button.y));
-
-      ScenePoint2D p = e.GetMainPosition().Apply(scene.GetCanvasToSceneTransform());
-
-      char buf[64];
-      sprintf(buf, "(%0.02f,%0.02f)", p.GetX(), p.GetY());
-
-      if (scene.HasLayer(LAYER_POSITION))
-      {
-        TextSceneLayer& layer =
-          dynamic_cast<TextSceneLayer&>(scene.GetLayer(LAYER_POSITION));
-        layer.SetText(buf);
-        layer.SetPosition(p.GetX(), p.GetY());
-      }
-      else
-      {
-        std::unique_ptr<TextSceneLayer> 
-          layer(new TextSceneLayer);
-        layer->SetColor(0, 255, 0);
-        layer->SetText(buf);
-        layer->SetBorder(20);
-        layer->SetAnchor(BitmapAnchor_BottomCenter);
-        layer->SetPosition(p.GetX(), p.GetY());
-        scene.SetLayer(LAYER_POSITION, layer.release());
-      }
-    }
-    else
-    {
-      scene.DeleteLayer(LAYER_POSITION);
-    }
-  }
-  else if (event.type == SDL_MOUSEBUTTONDOWN)
-  {
-    PointerEvent e;
-    e.AddPosition(viewport.GetPixelCenterCoordinates(event.button.x, event.button.y));
-
-    switch (event.button.button)
-    {
-      case SDL_BUTTON_MIDDLE:
-        activeTracker = boost::make_shared<PanSceneTracker>(controller, e);
-        break;
-
-      case SDL_BUTTON_RIGHT:
-        activeTracker = boost::make_shared<ZoomSceneTracker>
-          (controller, e, viewport.GetCanvasHeight());
-        break;
-
-      case SDL_BUTTON_LEFT:
-        activeTracker = boost::make_shared<RotateSceneTracker>(controller, e);
-        break;
-
-      default:
-        break;
-    }
-  }
-  else if (event.type == SDL_KEYDOWN &&
-           event.key.repeat == 0 /* Ignore key bounce */)
-  {
-    switch (event.key.keysym.sym)
-    {
-      case SDLK_s:
-        controller->FitContent(viewport.GetCanvasWidth(), 
-                               viewport.GetCanvasHeight());
-        break;
-              
-      case SDLK_c:
-        TakeScreenshot("screenshot.png", scene, 
-                       viewport.GetCanvasWidth(), 
-                       viewport.GetCanvasHeight());
-        break;
-              
-      default:
-        break;
-    }
-  }
-}
-
-#if OPENGL_ENABLED==1
-static void GLAPIENTRY
-OpenGLMessageCallback(GLenum source,
-                      GLenum type,
-                      GLuint id,
-                      GLenum severity,
-                      GLsizei length,
-                      const GLchar* message,
-                      const void* userParam )
-{
-  if (severity != GL_DEBUG_SEVERITY_NOTIFICATION)
-  {
-    fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
-            ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ),
-            type, severity, message );
-  }
-}
-#endif
-
-void Run(OrthancStone::MessageBroker& broker,
-         OrthancStone::SdlViewport& viewport)
-{
-  using namespace OrthancStone;
-  
-  boost::shared_ptr<ViewportController> controller(
-    new ViewportController(boost::make_shared<UndoStack>(), broker, viewport));
-  
-#if OPENGL_ENABLED==1
-  glEnable(GL_DEBUG_OUTPUT);
-  glDebugMessageCallback(OpenGLMessageCallback, 0);
-#endif
-
-  boost::shared_ptr<IFlexiblePointerTracker> tracker;
-
-  bool firstShown = true;
-  bool stop = false;
-  while (!stop)
-  {
-    viewport.Refresh();
-
-    SDL_Event event;
-    while (!stop &&
-           SDL_PollEvent(&event))
-    {
-      if (event.type == SDL_QUIT)
-      {
-        stop = true;
-        break;
-      }
-      else if (event.type == SDL_MOUSEMOTION)
-      {
-        if (tracker)
-        {
-          PointerEvent e;
-          e.AddPosition(viewport.GetPixelCenterCoordinates(
-            event.button.x, event.button.y));
-          tracker->PointerMove(e);
-        }
-      }
-      else if (event.type == SDL_MOUSEBUTTONUP)
-      {
-        if (tracker)
-        {
-          PointerEvent e;
-          e.AddPosition(viewport.GetPixelCenterCoordinates(
-            event.button.x, event.button.y));
-          tracker->PointerUp(e);
-          if(!tracker->IsAlive())
-            tracker.reset();
-        }
-      }
-      else if (event.type == SDL_WINDOWEVENT)
-      {
-        switch (event.window.event)
-        {
-          case SDL_WINDOWEVENT_SIZE_CHANGED:
-            tracker.reset();
-            viewport.UpdateSize(event.window.data1, event.window.data2);
-            break;
-
-          case SDL_WINDOWEVENT_SHOWN:
-            if (firstShown)
-            {
-              // Once the window is first shown, fit the content to its size
-              controller->FitContent(viewport.GetCanvasWidth(), viewport.GetCanvasHeight());
-              firstShown = false;
-            }
-            
-            break;
-
-          default:
-            break;
-        }
-      }
-      else if (event.type == SDL_KEYDOWN &&
-               event.key.repeat == 0 /* Ignore key bounce */)
-      {
-        switch (event.key.keysym.sym)
-        {
-          case SDLK_f:
-            viewport.GetWindow().ToggleMaximize();
-            break;
-              
-          case SDLK_q:
-            stop = true;
-            break;
-
-          default:
-            break;
-        }
-      }
-      
-      HandleApplicationEvent(event, controller, tracker);
-    }
-
-    SDL_Delay(1);
-  }
-}
-
-
-
-
-/**
- * IMPORTANT: The full arguments to "main()" are needed for SDL on
- * Windows. Otherwise, one gets the linking error "undefined reference
- * to `SDL_main'". https://wiki.libsdl.org/FAQWindows
- **/
-int main(int argc, char* argv[])
-{
-  OrthancStone::StoneInitialize();
-  Orthanc::Logging::EnableInfoLevel(true);
-
-  try
-  {
-#if OPENGL_ENABLED==1
-    OrthancStone::SdlOpenGLViewport viewport("Hello", 1024, 768);
-#else
-    OrthancStone::SdlCairoViewport viewport("Hello", 1024, 768);
-#endif
-    PrepareScene(viewport.GetScene());
-
-    viewport.GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, 
-                                     FONT_SIZE, Orthanc::Encoding_Latin1);
-    
-    OrthancStone::MessageBroker broker;
-    Run(broker, viewport);
-  }
-  catch (Orthanc::OrthancException& e)
-  {
-    LOG(ERROR) << "EXCEPTION: " << e.What();
-  }
-
-  OrthancStone::StoneFinalize();
-
-  return 0;
-}
--- a/Samples/Sdl/CMakeLists.txt	Tue Apr 07 14:29:01 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,129 +0,0 @@
-cmake_minimum_required(VERSION 2.8.3)
-
-#####################################################################
-## 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_SDL_CONSOLE OFF CACHE BOOL "Enable the use of the MIT-licensed SDL_Console")
-SET(ENABLE_GOOGLE_TEST OFF)
-SET(ENABLE_LOCALE ON)
-SET(ENABLE_SDL ON)
-SET(ENABLE_WEB_CLIENT ON)
-SET(ORTHANC_SANDBOXED OFF)
-LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options)
-
-include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneConfiguration.cmake)
-
-add_definitions(
-  -DORTHANC_ENABLE_LOGGING_PLUGIN=0
-  )
-
-
-#####################################################################
-## Build the samples
-#####################################################################
-
-add_library(OrthancStone STATIC
-  ${ORTHANC_STONE_SOURCES}
-  )
-
-#
-# BasicScene
-# 
-
-add_executable(BasicScene
-  BasicScene.cpp
-  )
-
-target_link_libraries(BasicScene OrthancStone)
-
-#
-# TrackerSample
-# 
-
-LIST(APPEND TRACKERSAMPLE_SOURCE "TrackerSample.cpp")
-LIST(APPEND TRACKERSAMPLE_SOURCE "TrackerSampleApp.cpp")
-LIST(APPEND TRACKERSAMPLE_SOURCE "TrackerSampleApp.h")
-
-if (MSVC AND MSVC_VERSION GREATER 1700)
-  LIST(APPEND TRACKERSAMPLE_SOURCE "cpp.hint")
-endif()
-
-add_executable(TrackerSample
-  ${TRACKERSAMPLE_SOURCE}
-  )
-
-target_link_libraries(TrackerSample OrthancStone)
-
-#
-# Loader
-# 
-
-add_executable(Loader
-  Loader.cpp
-  )
-
-target_link_libraries(Loader OrthancStone)
-
-#
-# FusionMprSdl
-# 
-
-add_executable(FusionMprSdl
-  FusionMprSdl.cpp
-  FusionMprSdl.h
-)
-
-target_link_libraries(FusionMprSdl OrthancStone)
-
-#
-# Multiplatform Basic Scene
-#
-
-LIST(APPEND MP_BASIC_SCENE_SOURCE "../MultiPlatform/BasicScene/BasicScene.cpp")
-LIST(APPEND MP_BASIC_SCENE_SOURCE "../MultiPlatform/BasicScene/BasicScene.h")
-LIST(APPEND MP_BASIC_SCENE_SOURCE "../MultiPlatform/BasicScene/mainSdl.cpp")
-
-if (MSVC AND MSVC_VERSION GREATER 1700)
-  LIST(APPEND MP_BASIC_SCENE_SOURCE "cpp.hint")
-endif()
-
-add_executable(MpBasicScene
-  ${MP_BASIC_SCENE_SOURCE}
-  )
-
-target_include_directories(MpBasicScene PUBLIC ${ORTHANC_STONE_ROOT})
-target_link_libraries(MpBasicScene OrthancStone)
--- a/Samples/Sdl/FusionMprSdl.cpp	Tue Apr 07 14:29:01 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,805 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 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 "FusionMprSdl.h"
-
-#include "../../Framework/OpenGL/SdlOpenGLContext.h"
-
-#include "../../Framework/StoneInitialization.h"
-
-#include "../../Framework/Scene2D/CairoCompositor.h"
-#include "../../Framework/Scene2D/ColorTextureSceneLayer.h"
-#include "../../Framework/Scene2D/OpenGLCompositor.h"
-#include "../../Framework/Scene2D/PanSceneTracker.h"
-#include "../../Framework/Scene2D/ZoomSceneTracker.h"
-#include "../../Framework/Scene2D/RotateSceneTracker.h"
-
-#include "../../Framework/Scene2DViewport/UndoStack.h"
-#include "../../Framework/Scene2DViewport/CreateLineMeasureTracker.h"
-#include "../../Framework/Scene2DViewport/CreateAngleMeasureTracker.h"
-#include "../../Framework/Scene2DViewport/IFlexiblePointerTracker.h"
-#include "../../Framework/Scene2DViewport/MeasureTool.h"
-#include "../../Framework/Scene2DViewport/PredeclaredTypes.h"
-
-#include "../../Framework/Volumes/VolumeSceneLayerSource.h"
-
-#include <Core/Images/Image.h>
-#include <Core/Images/ImageProcessing.h>
-#include <Core/Images/PngWriter.h>
-#include <Core/Logging.h>
-#include <Core/OrthancException.h>
-
-#include <boost/shared_ptr.hpp>
-#include <boost/weak_ptr.hpp>
-#include <boost/make_shared.hpp>
-
-#include <stdio.h>
-#include "../../Framework/Oracle/GetOrthancWebViewerJpegCommand.h"
-#include "../../Framework/Oracle/ThreadedOracle.h"
-#include "../../Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h"
-#include "../../Framework/Loaders/OrthancMultiframeVolumeLoader.h"
-#include "../../Framework/Loaders/DicomStructureSetLoader.h"
-#include "../../Framework/Scene2D/GrayscaleStyleConfigurator.h"
-#include "../../Framework/Scene2D/LookupTableStyleConfigurator.h"
-#include "../../Framework/Volumes/DicomVolumeImageMPRSlicer.h"
-#include "Core/SystemToolbox.h"
-
-namespace OrthancStone
-{
-  const char* FusionMprMeasureToolToString(size_t i)
-  {
-    static const char* descs[] = {
-      "FusionMprGuiTool_Rotate",
-      "FusionMprGuiTool_Pan",
-      "FusionMprGuiTool_Zoom",
-      "FusionMprGuiTool_LineMeasure",
-      "FusionMprGuiTool_CircleMeasure",
-      "FusionMprGuiTool_AngleMeasure",
-      "FusionMprGuiTool_EllipseMeasure",
-      "FusionMprGuiTool_LAST"
-    };
-    if (i >= FusionMprGuiTool_LAST)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Wrong tool index");
-    }
-    return descs[i];
-  }
-
-  Scene2D& FusionMprSdlApp::GetScene()
-  {
-    return controller_->GetScene();
-  }
-
-  const Scene2D& FusionMprSdlApp::GetScene() const
-  {
-    return controller_->GetScene();
-  }
-
-  void FusionMprSdlApp::SelectNextTool()
-  {
-    currentTool_ = static_cast<FusionMprGuiTool>(currentTool_ + 1);
-    if (currentTool_ == FusionMprGuiTool_LAST)
-      currentTool_ = static_cast<FusionMprGuiTool>(0);;
-    printf("Current tool is now: %s\n", FusionMprMeasureToolToString(currentTool_));
-  }
-
-  void FusionMprSdlApp::DisplayInfoText()
-  {
-    // do not try to use stuff too early!
-    ICompositor* pCompositor = &(viewport_.GetCompositor());
-    if (pCompositor == NULL)
-      return;
-
-    std::stringstream msg;
-	
-	for (std::map<std::string, std::string>::const_iterator kv = infoTextMap_.begin();
-		kv != infoTextMap_.end(); ++kv)
-    {
-      msg << kv->first << " : " << kv->second << std::endl;
-    }
-	std::string msgS = msg.str();
-
-    TextSceneLayer* layerP = NULL;
-    if (GetScene().HasLayer(FIXED_INFOTEXT_LAYER_ZINDEX))
-    {
-      TextSceneLayer& layer = dynamic_cast<TextSceneLayer&>(
-        GetScene().GetLayer(FIXED_INFOTEXT_LAYER_ZINDEX));
-      layerP = &layer;
-    }
-    else
-    {
-      std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer);
-      layerP = layer.get();
-      layer->SetColor(0, 255, 0);
-      layer->SetFontIndex(1);
-      layer->SetBorder(20);
-      layer->SetAnchor(BitmapAnchor_TopLeft);
-      //layer->SetPosition(0,0);
-      GetScene().SetLayer(FIXED_INFOTEXT_LAYER_ZINDEX, layer.release());
-    }
-    // position the fixed info text in the upper right corner
-    layerP->SetText(msgS.c_str());
-    double cX = viewport_.GetCompositor().GetCanvasWidth() * (-0.5);
-    double cY = viewport_.GetCompositor().GetCanvasHeight() * (-0.5);
-    GetScene().GetCanvasToSceneTransform().Apply(cX,cY);
-    layerP->SetPosition(cX, cY);
-  }
-
-  void FusionMprSdlApp::DisplayFloatingCtrlInfoText(const PointerEvent& e)
-  {
-    ScenePoint2D p = e.GetMainPosition().Apply(GetScene().GetCanvasToSceneTransform());
-
-    char buf[128];
-    sprintf(buf, "S:(%0.02f,%0.02f) C:(%0.02f,%0.02f)", 
-      p.GetX(), p.GetY(), 
-      e.GetMainPosition().GetX(), e.GetMainPosition().GetY());
-
-    if (GetScene().HasLayer(FLOATING_INFOTEXT_LAYER_ZINDEX))
-    {
-      TextSceneLayer& layer =
-        dynamic_cast<TextSceneLayer&>(GetScene().GetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX));
-      layer.SetText(buf);
-      layer.SetPosition(p.GetX(), p.GetY());
-    }
-    else
-    {
-      std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer);
-      layer->SetColor(0, 255, 0);
-      layer->SetText(buf);
-      layer->SetBorder(20);
-      layer->SetAnchor(BitmapAnchor_BottomCenter);
-      layer->SetPosition(p.GetX(), p.GetY());
-      GetScene().SetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX, layer.release());
-    }
-  }
-
-  void FusionMprSdlApp::HideInfoText()
-  {
-    GetScene().DeleteLayer(FLOATING_INFOTEXT_LAYER_ZINDEX);
-  }
-
-  void FusionMprSdlApp::HandleApplicationEvent(
-    const SDL_Event & event)
-  {
-    DisplayInfoText();
-
-    if (event.type == SDL_MOUSEMOTION)
-    {
-      int scancodeCount = 0;
-      const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount);
-
-      if (activeTracker_.get() == NULL &&
-        SDL_SCANCODE_LALT < scancodeCount &&
-        keyboardState[SDL_SCANCODE_LALT])
-      {
-        // The "left-ctrl" key is down, while no tracker is present
-        // Let's display the info text
-        PointerEvent e;
-        e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(
-          event.button.x, event.button.y));
-
-        DisplayFloatingCtrlInfoText(e);
-      }
-      else
-      {
-        HideInfoText();
-        //LOG(TRACE) << "(event.type == SDL_MOUSEMOTION)";
-        if (activeTracker_.get() != NULL)
-        {
-          //LOG(TRACE) << "(activeTracker_.get() != NULL)";
-          PointerEvent e;
-          e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(
-            event.button.x, event.button.y));
-          
-          //LOG(TRACE) << "event.button.x = " << event.button.x << "     " <<
-          //  "event.button.y = " << event.button.y;
-          LOG(TRACE) << "activeTracker_->PointerMove(e); " <<
-            e.GetMainPosition().GetX() << " " << e.GetMainPosition().GetY();
-          
-          activeTracker_->PointerMove(e);
-          if (!activeTracker_->IsAlive())
-            activeTracker_.reset();
-        }
-      }
-    }
-    else if (event.type == SDL_MOUSEBUTTONUP)
-    {
-      if (activeTracker_)
-      {
-        PointerEvent e;
-        e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(event.button.x, event.button.y));
-        activeTracker_->PointerUp(e);
-        if (!activeTracker_->IsAlive())
-          activeTracker_.reset();
-      }
-    }
-    else if (event.type == SDL_MOUSEBUTTONDOWN)
-    {
-      PointerEvent e;
-      e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(
-        event.button.x, event.button.y));
-      if (activeTracker_)
-      {
-        activeTracker_->PointerDown(e);
-        if (!activeTracker_->IsAlive())
-          activeTracker_.reset();
-      }
-      else
-      {
-        // we ATTEMPT to create a tracker if need be
-        activeTracker_ = CreateSuitableTracker(event, e);
-      }
-    }
-    else if (event.type == SDL_KEYDOWN &&
-      event.key.repeat == 0 /* Ignore key bounce */)
-    {
-      switch (event.key.keysym.sym)
-      {
-      case SDLK_ESCAPE:
-        if (activeTracker_)
-        {
-          activeTracker_->Cancel();
-          if (!activeTracker_->IsAlive())
-            activeTracker_.reset();
-        }
-        break;
-
-      case SDLK_t:
-        if (!activeTracker_)
-          SelectNextTool();
-        else
-        {
-          LOG(WARNING) << "You cannot change the active tool when an interaction"
-            " is taking place";
-        }
-        break;
-      case SDLK_s:
-        controller_->FitContent(viewport_.GetCompositor().GetCanvasWidth(),
-          viewport_.GetCompositor().GetCanvasHeight());
-        break;
-
-      case SDLK_z:
-        LOG(TRACE) << "SDLK_z has been pressed. event.key.keysym.mod == " << event.key.keysym.mod;
-        if (event.key.keysym.mod & KMOD_CTRL)
-        {
-          if (controller_->CanUndo())
-          {
-            LOG(TRACE) << "Undoing...";
-            controller_->Undo();
-          }
-          else
-          {
-            LOG(WARNING) << "Nothing to undo!!!";
-          }
-        }
-        break;
-
-      case SDLK_y:
-        LOG(TRACE) << "SDLK_y has been pressed. event.key.keysym.mod == " << event.key.keysym.mod;
-        if (event.key.keysym.mod & KMOD_CTRL)
-        {
-          if (controller_->CanRedo())
-          {
-            LOG(TRACE) << "Redoing...";
-            controller_->Redo();
-          }
-          else
-          {
-            LOG(WARNING) << "Nothing to redo!!!";
-          }
-        }
-        break;
-
-      case SDLK_c:
-        TakeScreenshot(
-          "screenshot.png",
-          viewport_.GetCompositor().GetCanvasWidth(),
-          viewport_.GetCompositor().GetCanvasHeight());
-        break;
-
-      default:
-        break;
-      }
-    }
-  }
-
-
-  void FusionMprSdlApp::OnSceneTransformChanged(
-    const ViewportController::SceneTransformChanged& message)
-  {
-    DisplayInfoText();
-  }
-
-  boost::shared_ptr<IFlexiblePointerTracker> FusionMprSdlApp::CreateSuitableTracker(
-    const SDL_Event & event,
-    const PointerEvent & e)
-  {
-    using namespace Orthanc;
-
-    switch (event.button.button)
-    {
-    case SDL_BUTTON_MIDDLE:
-      return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker
-        (controller_, e));
-
-    case SDL_BUTTON_RIGHT:
-      return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker
-        (controller_, e, viewport_.GetCompositor().GetCanvasHeight()));
-
-    case SDL_BUTTON_LEFT:
-    {
-      //LOG(TRACE) << "CreateSuitableTracker: case SDL_BUTTON_LEFT:";
-      // TODO: we need to iterate on the set of measuring tool and perform
-      // a hit test to check if a tracker needs to be created for edition.
-      // Otherwise, depending upon the active tool, we might want to create
-      // a "measuring tool creation" tracker
-
-      // TODO: if there are conflicts, we should prefer a tracker that 
-      // pertains to the type of measuring tool currently selected (TBD?)
-      boost::shared_ptr<IFlexiblePointerTracker> hitTestTracker = TrackerHitTest(e);
-
-      if (hitTestTracker != NULL)
-      {
-        //LOG(TRACE) << "hitTestTracker != NULL";
-        return hitTestTracker;
-      }
-      else
-      {
-        switch (currentTool_)
-        {
-        case FusionMprGuiTool_Rotate:
-          //LOG(TRACE) << "Creating RotateSceneTracker";
-          return boost::shared_ptr<IFlexiblePointerTracker>(new RotateSceneTracker(
-            controller_, e));
-        case FusionMprGuiTool_Pan:
-          return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker(
-            controller_, e));
-        case FusionMprGuiTool_Zoom:
-          return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker(
-            controller_, e, viewport_.GetCompositor().GetCanvasHeight()));
-        //case GuiTool_AngleMeasure:
-        //  return new AngleMeasureTracker(GetScene(), e);
-        //case GuiTool_CircleMeasure:
-        //  return new CircleMeasureTracker(GetScene(), e);
-        //case GuiTool_EllipseMeasure:
-        //  return new EllipseMeasureTracker(GetScene(), e);
-        case FusionMprGuiTool_LineMeasure:
-          return boost::shared_ptr<IFlexiblePointerTracker>(new CreateLineMeasureTracker(
-            IObserver::GetBroker(), controller_, e));
-        case FusionMprGuiTool_AngleMeasure:
-          return boost::shared_ptr<IFlexiblePointerTracker>(new CreateAngleMeasureTracker(
-            IObserver::GetBroker(), controller_, e));
-        case FusionMprGuiTool_CircleMeasure:
-          LOG(ERROR) << "Not implemented yet!";
-          return boost::shared_ptr<IFlexiblePointerTracker>();
-        case FusionMprGuiTool_EllipseMeasure:
-          LOG(ERROR) << "Not implemented yet!";
-          return boost::shared_ptr<IFlexiblePointerTracker>();
-        default:
-          throw OrthancException(ErrorCode_InternalError, "Wrong tool!");
-        }
-      }
-    }
-    default:
-      return boost::shared_ptr<IFlexiblePointerTracker>();
-    }
-  }
-
-
-  FusionMprSdlApp::FusionMprSdlApp(MessageBroker& broker)
-    : IObserver(broker)
-    , broker_(broker)
-    , oracleObservable_(broker)
-    , oracle_(*this)
-    , currentTool_(FusionMprGuiTool_Rotate)
-    , undoStack_(new UndoStack)
-    , viewport_("Hello", 1024, 1024, false) // False means we do NOT let Windows treat this as a legacy application that needs to be scaled
-  {
-    //oracleObservable.RegisterObserverCallback
-    //(new Callable
-    //  <FusionMprSdlApp, SleepOracleCommand::TimeoutMessage>(*this, &FusionMprSdlApp::Handle));
-
-    //oracleObservable.RegisterObserverCallback
-    //(new Callable
-    //  <Toto, GetOrthancImageCommand::SuccessMessage>(*this, &FusionMprSdlApp::Handle));
-
-    //oracleObservable.RegisterObserverCallback
-    //(new Callable
-    //  <FusionMprSdlApp, GetOrthancWebViewerJpegCommand::SuccessMessage>(*this, &ToFusionMprSdlAppto::Handle));
-
-    oracleObservable_.RegisterObserverCallback
-    (new Callable
-      <FusionMprSdlApp, OracleCommandExceptionMessage>(*this, &FusionMprSdlApp::Handle));
-    
-    controller_ = boost::shared_ptr<ViewportController>(
-      new ViewportController(undoStack_, broker_, viewport_));
-
-    controller_->RegisterObserverCallback(
-      new Callable<FusionMprSdlApp, ViewportController::SceneTransformChanged>
-      (*this, &FusionMprSdlApp::OnSceneTransformChanged));
-
-    TEXTURE_2x2_1_ZINDEX = 1;
-    TEXTURE_1x1_ZINDEX = 2;
-    TEXTURE_2x2_2_ZINDEX = 3;
-    LINESET_1_ZINDEX = 4;
-    LINESET_2_ZINDEX = 5;
-    FLOATING_INFOTEXT_LAYER_ZINDEX = 6;
-    FIXED_INFOTEXT_LAYER_ZINDEX = 7;
-  }
-
-  void FusionMprSdlApp::PrepareScene()
-  {
-    // Texture of 2x2 size
-    {
-      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;
-
-      GetScene().SetLayer(TEXTURE_2x2_1_ZINDEX, new ColorTextureSceneLayer(i));
-    }
-  }
-
-  void FusionMprSdlApp::DisableTracker()
-  {
-    if (activeTracker_)
-    {
-      activeTracker_->Cancel();
-      activeTracker_.reset();
-    }
-  }
-
-  void FusionMprSdlApp::TakeScreenshot(const std::string& target,
-    unsigned int canvasWidth,
-    unsigned int canvasHeight)
-  {
-    CairoCompositor compositor(GetScene(), canvasWidth, canvasHeight);
-    compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, FONT_SIZE_0, Orthanc::Encoding_Latin1);
-    compositor.Refresh();
-
-    Orthanc::ImageAccessor canvas;
-    compositor.GetCanvas().GetReadOnlyAccessor(canvas);
-
-    Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false);
-    Orthanc::ImageProcessing::Convert(png, canvas);
-
-    Orthanc::PngWriter writer;
-    writer.WriteToFile(target, png);
-  }
-
-
-  boost::shared_ptr<IFlexiblePointerTracker> FusionMprSdlApp::TrackerHitTest(const PointerEvent & e)
-  {
-    // std::vector<boost::shared_ptr<MeasureTool>> measureTools_;
-    return boost::shared_ptr<IFlexiblePointerTracker>();
-  }
-
-  static void GLAPIENTRY
-    OpenGLMessageCallback(GLenum source,
-      GLenum type,
-      GLuint id,
-      GLenum severity,
-      GLsizei length,
-      const GLchar* message,
-      const void* userParam)
-  {
-    if (severity != GL_DEBUG_SEVERITY_NOTIFICATION)
-    {
-      fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
-        (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""),
-        type, severity, message);
-    }
-  }
-
-  static bool g_stopApplication = false;
-  
-
-  void FusionMprSdlApp::Handle(const DicomVolumeImage::GeometryReadyMessage& message)
-  {
-    printf("Geometry ready\n");
-
-    //plane_ = message.GetOrigin().GetGeometry().GetSagittalGeometry();
-    //plane_ = message.GetOrigin().GetGeometry().GetAxialGeometry();
-    plane_ = message.GetOrigin().GetGeometry().GetCoronalGeometry();
-    plane_.SetOrigin(message.GetOrigin().GetGeometry().GetCoordinates(0.5f, 0.5f, 0.5f));
-
-    //Refresh();
-  }
-
-
-  void FusionMprSdlApp::Handle(const OracleCommandExceptionMessage& message)
-  {
-    printf("EXCEPTION: [%s] on command type %d\n", message.GetException().What(), message.GetCommand().GetType());
-
-    switch (message.GetCommand().GetType())
-    {
-    case IOracleCommand::Type_GetOrthancWebViewerJpeg:
-      printf("URI: [%s]\n", dynamic_cast<const GetOrthancWebViewerJpegCommand&>
-        (message.GetCommand()).GetUri().c_str());
-      break;
-
-    default:
-      break;
-    }
-  }
-
-  void FusionMprSdlApp::SetVolume1(int depth,
-    const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume,
-    OrthancStone::ILayerStyleConfigurator* style)
-  {
-    source1_.reset(new OrthancStone::VolumeSceneLayerSource(controller_->GetScene(), depth, volume));
-
-    if (style != NULL)
-    {
-      source1_->SetConfigurator(style);
-    }
-  }
-
-  void FusionMprSdlApp::SetVolume2(int depth,
-    const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume,
-    OrthancStone::ILayerStyleConfigurator* style)
-  {
-    source2_.reset(new OrthancStone::VolumeSceneLayerSource(controller_->GetScene(), depth, volume));
-
-    if (style != NULL)
-    {
-      source2_->SetConfigurator(style);
-    }
-  }
-
-  void FusionMprSdlApp::SetStructureSet(int depth,
-    const boost::shared_ptr<OrthancStone::DicomStructureSetLoader>& volume)
-  {
-    source3_.reset(new OrthancStone::VolumeSceneLayerSource(controller_->GetScene(), depth, volume));
-  }
-  
-  void FusionMprSdlApp::Run()
-  {
-    // False means we do NOT let Windows treat this as a legacy application
-    // that needs to be scaled
-    controller_->FitContent(viewport_.GetCanvasWidth(), viewport_.GetCanvasHeight());
-
-    glEnable(GL_DEBUG_OUTPUT);
-    glDebugMessageCallback(OpenGLMessageCallback, 0);
-
-    viewport_.GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT,
-      FONT_SIZE_0, Orthanc::Encoding_Latin1);
-    viewport_.GetCompositor().SetFont(1, Orthanc::EmbeddedResources::UBUNTU_FONT,
-      FONT_SIZE_1, Orthanc::Encoding_Latin1);
-
-
-    //////// from loader
-    {
-      Orthanc::WebServiceParameters p;
-      //p.SetUrl("http://localhost:8043/");
-      p.SetCredentials("orthanc", "orthanc");
-      oracle_.SetOrthancParameters(p);
-    }
-
-    //////// from Run
-
-    boost::shared_ptr<DicomVolumeImage>  ct(new DicomVolumeImage);
-    boost::shared_ptr<DicomVolumeImage>  dose(new DicomVolumeImage);
-
-
-    boost::shared_ptr<OrthancSeriesVolumeProgressiveLoader> ctLoader;
-    boost::shared_ptr<OrthancMultiframeVolumeLoader> doseLoader;
-    boost::shared_ptr<DicomStructureSetLoader>  rtstructLoader;
-
-    {
-      ctLoader.reset(new OrthancSeriesVolumeProgressiveLoader(ct, oracle_, oracleObservable_));
-      doseLoader.reset(new OrthancMultiframeVolumeLoader(dose, oracle_, oracleObservable_));
-      rtstructLoader.reset(new DicomStructureSetLoader(oracle_, oracleObservable_));
-    }
-
-    //toto->SetReferenceLoader(*ctLoader);
-    //doseLoader->RegisterObserverCallback
-    //(new Callable
-    //  <FusionMprSdlApp, DicomVolumeImage::GeometryReadyMessage>(*this, &FusionMprSdlApp::Handle));
-    ctLoader->RegisterObserverCallback
-    (new Callable
-      <FusionMprSdlApp, DicomVolumeImage::GeometryReadyMessage>(*this, &FusionMprSdlApp::Handle));
-
-    this->SetVolume1(0, ctLoader, new GrayscaleStyleConfigurator);
-
-    {
-      std::unique_ptr<LookupTableStyleConfigurator> config(new LookupTableStyleConfigurator);
-      config->SetLookupTable(Orthanc::EmbeddedResources::COLORMAP_HOT);
-
-      boost::shared_ptr<DicomVolumeImageMPRSlicer> tmp(new DicomVolumeImageMPRSlicer(dose));
-      this->SetVolume2(1, tmp, config.release());
-    }
-
-    this->SetStructureSet(2, rtstructLoader);
-
-#if 1
-    /*
-    BGO data
-    http://localhost:8042/twiga-orthanc-viewer-demo/twiga-orthanc-viewer-demo.html?ct-series=a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa
-    &
-    dose-instance=830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb
-    &
-    struct-instance=54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9
-    */
-    ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa");  // CT
-    doseLoader->LoadInstance("830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb");  // RT-DOSE
-    rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9");  // RT-STRUCT
-#else
-    //ctLoader->LoadSeries("cb3ea4d1-d08f3856-ad7b6314-74d88d77-60b05618");  // CT
-    //doseLoader->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6");  // RT-DOSE
-    //rtstructLoader->LoadInstance("83d9c0c3-913a7fee-610097d7-cbf0522d-fd75bee6");  // RT-STRUCT
-
-    // 2017-05-16
-    ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa");  // CT
-    doseLoader->LoadInstance("eac822ef-a395f94e-e8121fe0-8411fef8-1f7bffad");  // RT-DOSE
-    rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9");  // RT-STRUCT
-#endif
-
-    oracle_.Start();
-
-//// END from loader
-
-    while (!g_stopApplication)
-    {
-      viewport_.GetCompositor().Refresh();
-
-//////// from loader
-      if (source1_.get() != NULL)
-      {
-        source1_->Update(plane_);
-      }
-
-      if (source2_.get() != NULL)
-      {
-        source2_->Update(plane_);
-      }
-
-      if (source3_.get() != NULL)
-      {
-        source3_->Update(plane_);
-      }
-//// END from loader
-
-      SDL_Event event;
-      while (!g_stopApplication && SDL_PollEvent(&event))
-      {
-        if (event.type == SDL_QUIT)
-        {
-          g_stopApplication = true;
-          break;
-        }
-        else if (event.type == SDL_WINDOWEVENT &&
-          event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
-        {
-          DisableTracker(); // was: tracker.reset(NULL);
-        }
-        else if (event.type == SDL_KEYDOWN &&
-          event.key.repeat == 0 /* Ignore key bounce */)
-        {
-          switch (event.key.keysym.sym)
-          {
-          case SDLK_f:
-            viewport_.GetWindow().ToggleMaximize();
-            break;
-
-          case SDLK_s:
-            controller_->FitContent(viewport_.GetCanvasWidth(), viewport_.GetCanvasHeight());
-            break;
-
-          case SDLK_q:
-            g_stopApplication = true;
-            break;
-          default:
-            break;
-          }
-        }
-        HandleApplicationEvent(event);
-      }
-      SDL_Delay(1);
-    }
-
-    //// from loader
-
-    //Orthanc::SystemToolbox::ServerBarrier();
-
-    /**
-     * WARNING => The oracle must be stopped BEFORE the objects using
-     * it are destroyed!!! This forces to wait for the completion of
-     * the running callback methods. Otherwise, the callbacks methods
-     * might still be running while their parent object is destroyed,
-     * resulting in crashes. This is very visible if adding a sleep(),
-     * as in (*).
-     **/
-
-    oracle_.Stop();
-    //// END from loader
-  }
-
-  void FusionMprSdlApp::SetInfoDisplayMessage(
-    std::string key, std::string value)
-  {
-    if (value == "")
-      infoTextMap_.erase(key);
-    else
-      infoTextMap_[key] = value;
-    DisplayInfoText();
-  }
-
-}
-
-
-boost::weak_ptr<OrthancStone::FusionMprSdlApp> g_app;
-
-void FusionMprSdl_SetInfoDisplayMessage(std::string key, std::string value)
-{
-  boost::shared_ptr<OrthancStone::FusionMprSdlApp> app = g_app.lock();
-  if (app)
-  {
-    app->SetInfoDisplayMessage(key, value);
-  }
-}
-
-/**
- * IMPORTANT: The full arguments to "main()" are needed for SDL on
- * Windows. Otherwise, one gets the linking error "undefined reference
- * to `SDL_main'". https://wiki.libsdl.org/FAQWindows
- **/
-int main(int argc, char* argv[])
-{
-  using namespace OrthancStone;
-
-  StoneInitialize();
-  Orthanc::Logging::EnableInfoLevel(true);
-//  Orthanc::Logging::EnableTraceLevel(true);
-
-  try
-  {
-    OrthancStone::MessageBroker broker;
-    boost::shared_ptr<FusionMprSdlApp> app(new FusionMprSdlApp(broker));
-    g_app = app;
-    app->PrepareScene();
-    app->Run();
-  }
-  catch (Orthanc::OrthancException& e)
-  {
-    LOG(ERROR) << "EXCEPTION: " << e.What();
-  }
-
-  StoneFinalize();
-
-  return 0;
-}
-
-
--- a/Samples/Sdl/FusionMprSdl.h	Tue Apr 07 14:29:01 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,206 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 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 "../../Framework/Viewport/SdlViewport.h"
-
-#include "../../Framework/Messages/IObserver.h"
-#include "../../Framework/Messages/IMessageEmitter.h"
-#include "../../Framework/Oracle/OracleCommandExceptionMessage.h"
-#include "../../Framework/Scene2DViewport/ViewportController.h"
-#include "../../Framework/Volumes/DicomVolumeImage.h"
-#include "../../Framework/Oracle/ThreadedOracle.h"
-
-#include <boost/enable_shared_from_this.hpp>
-#include <boost/thread.hpp>
-#include <boost/noncopyable.hpp>
-
-#include <SDL.h>
-
-namespace OrthancStone
-{
-  class OpenGLCompositor;
-  class IVolumeSlicer;
-  class ILayerStyleConfigurator;
-  class DicomStructureSetLoader;
-  class IOracle;
-  class ThreadedOracle;
-  class VolumeSceneLayerSource;
-  class NativeFusionMprApplicationContext;
-  class SdlOpenGLViewport;
-   
-  enum FusionMprGuiTool
-  {
-    FusionMprGuiTool_Rotate = 0,
-    FusionMprGuiTool_Pan,
-    FusionMprGuiTool_Zoom,
-    FusionMprGuiTool_LineMeasure,
-    FusionMprGuiTool_CircleMeasure,
-    FusionMprGuiTool_AngleMeasure,
-    FusionMprGuiTool_EllipseMeasure,
-    FusionMprGuiTool_LAST
-  };
-
-  const char* MeasureToolToString(size_t i);
-
-  static const unsigned int FONT_SIZE_0 = 32;
-  static const unsigned int FONT_SIZE_1 = 24;
-
-  class Scene2D;
-  class UndoStack;
-
-  /**
-  This application subclasses IMessageEmitter to use a mutex before forwarding Oracle messages (that
-  can be sent from multiple threads)
-  */
-  class FusionMprSdlApp : public IObserver
-    , public boost::enable_shared_from_this<FusionMprSdlApp>
-    , public IMessageEmitter
-  {
-  public:
-    // 12 because.
-    FusionMprSdlApp(MessageBroker& broker);
-
-    void PrepareScene();
-    void Run();
-    void SetInfoDisplayMessage(std::string key, std::string value);
-    void DisableTracker();
-
-    Scene2D&       GetScene();
-    const Scene2D& GetScene() const;
-
-    void HandleApplicationEvent(const SDL_Event& event);
-
-    /**
-    This method is called when the scene transform changes. It allows to
-    recompute the visual elements whose content depend upon the scene transform
-    */
-    void OnSceneTransformChanged(
-      const ViewportController::SceneTransformChanged& message);
-
-
-    virtual void EmitMessage(const IObserver& observer,
-      const IMessage& message) ORTHANC_OVERRIDE
-    {
-      try
-      {
-        boost::unique_lock<boost::shared_mutex>  lock(mutex_);
-        oracleObservable_.EmitMessage(observer, message);
-      }
-      catch (Orthanc::OrthancException& e)
-      {
-        LOG(ERROR) << "Exception while emitting a message: " << e.What();
-        throw;
-      }
-    }
-    
-  private:
-#if 1
-    // if threaded (not wasm)
-    MessageBroker& broker_;
-    IObservable oracleObservable_;
-    ThreadedOracle oracle_;
-    boost::shared_mutex mutex_; // to serialize messages from the ThreadedOracle
-#endif
-
-    void SelectNextTool();
-
-    /**
-    This returns a random point in the canvas part of the scene, but in
-    scene coordinates
-    */
-    ScenePoint2D GetRandomPointInScene() const;
-
-    boost::shared_ptr<IFlexiblePointerTracker> TrackerHitTest(const PointerEvent& e);
-
-    boost::shared_ptr<IFlexiblePointerTracker> CreateSuitableTracker(
-      const SDL_Event& event,
-      const PointerEvent& e);
-
-    void TakeScreenshot(
-      const std::string& target,
-      unsigned int canvasWidth,
-      unsigned int canvasHeight);
-
-    /**
-      This adds the command at the top of the undo stack
-    */
-    void Commit(boost::shared_ptr<TrackerCommand> cmd);
-    void Undo();
-    void Redo();
-
-
-    // TODO private
-    void Handle(const DicomVolumeImage::GeometryReadyMessage& message);
-    void Handle(const OracleCommandExceptionMessage& message);
-
-    void SetVolume1(
-      int depth,
-      const boost::shared_ptr<IVolumeSlicer>& volume,
-      ILayerStyleConfigurator* style);
-    
-    void SetVolume2(
-      int depth,
-      const boost::shared_ptr<IVolumeSlicer>& volume,
-      ILayerStyleConfigurator* style);
-
-    void SetStructureSet(
-      int depth, 
-      const boost::shared_ptr<DicomStructureSetLoader>& volume);
-
-
-
-  private:
-    void DisplayFloatingCtrlInfoText(const PointerEvent& e);
-    void DisplayInfoText();
-    void HideInfoText();
-
-  private:
-    CoordinateSystem3D  plane_;
-
-    boost::shared_ptr<VolumeSceneLayerSource>  source1_, source2_, source3_;
-
-    /**
-    WARNING: the measuring tools do store a reference to the scene, and it
-    paramount that the scene gets destroyed AFTER the measurement tools.
-    */
-    boost::shared_ptr<ViewportController> controller_;
-
-    std::map<std::string, std::string> infoTextMap_;
-    boost::shared_ptr<IFlexiblePointerTracker> activeTracker_;
-
-    //static const int LAYER_POSITION = 150;
-
-    int TEXTURE_2x2_1_ZINDEX;
-    int TEXTURE_1x1_ZINDEX;
-    int TEXTURE_2x2_2_ZINDEX;
-    int LINESET_1_ZINDEX;
-    int LINESET_2_ZINDEX;
-    int FLOATING_INFOTEXT_LAYER_ZINDEX;
-    int FIXED_INFOTEXT_LAYER_ZINDEX;
-
-    FusionMprGuiTool currentTool_;
-    boost::shared_ptr<UndoStack> undoStack_;
-    SdlOpenGLViewport viewport_;
-  };
-
-}
-
-
- 
\ No newline at end of file
--- a/Samples/Sdl/Loader.cpp	Tue Apr 07 14:29:01 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,518 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 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 "../../Framework/Loaders/DicomStructureSetLoader.h"
-#include "../../Framework/Loaders/OrthancMultiframeVolumeLoader.h"
-#include "../../Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h"
-#include "../../Framework/Oracle/SleepOracleCommand.h"
-#include "../../Framework/Oracle/ThreadedOracle.h"
-#include "../../Framework/Scene2D/CairoCompositor.h"
-#include "../../Framework/Scene2D/GrayscaleStyleConfigurator.h"
-#include "../../Framework/Scene2D/LookupTableStyleConfigurator.h"
-#include "../../Framework/StoneInitialization.h"
-#include "../../Framework/Volumes/VolumeSceneLayerSource.h"
-#include "../../Framework/Volumes/DicomVolumeImageMPRSlicer.h"
-#include "../../Framework/Volumes/DicomVolumeImageReslicer.h"
-
-// From Orthanc framework
-#include <Core/Images/ImageProcessing.h>
-#include <Core/Images/PngWriter.h>
-#include <Core/Logging.h>
-#include <Core/OrthancException.h>
-#include <Core/SystemToolbox.h>
-
-
-namespace OrthancStone
-{
-  class NativeApplicationContext : public IMessageEmitter
-  {
-  private:
-    boost::shared_mutex  mutex_;
-    MessageBroker        broker_;
-    IObservable          oracleObservable_;
-
-  public:
-    NativeApplicationContext() :
-      oracleObservable_(broker_)
-    {
-    }
-
-
-    virtual void EmitMessage(const IObserver& observer,
-                             const IMessage& message) ORTHANC_OVERRIDE
-    {
-      try
-      {
-        boost::unique_lock<boost::shared_mutex>  lock(mutex_);
-        oracleObservable_.EmitMessage(observer, message);
-      }
-      catch (Orthanc::OrthancException& e)
-      {
-        LOG(ERROR) << "Exception while emitting a message: " << e.What();
-      }
-    }
-
-
-    class ReaderLock : public boost::noncopyable
-    {
-    private:
-      NativeApplicationContext&                that_;
-      boost::shared_lock<boost::shared_mutex>  lock_;
-
-    public:
-      ReaderLock(NativeApplicationContext& that) : 
-        that_(that),
-        lock_(that.mutex_)
-      {
-      }
-    };
-
-
-    class WriterLock : public boost::noncopyable
-    {
-    private:
-      NativeApplicationContext&                that_;
-      boost::unique_lock<boost::shared_mutex>  lock_;
-
-    public:
-      WriterLock(NativeApplicationContext& that) : 
-        that_(that),
-        lock_(that.mutex_)
-      {
-      }
-
-      MessageBroker& GetBroker() 
-      {
-        return that_.broker_;
-      }
-
-      IObservable& GetOracleObservable()
-      {
-        return that_.oracleObservable_;
-      }
-    };
-  };
-}
-
-
-
-class Toto : public OrthancStone::IObserver
-{
-private:
-  OrthancStone::CoordinateSystem3D  plane_;
-  OrthancStone::IOracle&            oracle_;
-  OrthancStone::Scene2D             scene_;
-  std::unique_ptr<OrthancStone::VolumeSceneLayerSource>  source1_, source2_, source3_;
-
-
-  void Refresh()
-  {
-    if (source1_.get() != NULL)
-    {
-      source1_->Update(plane_);
-    }
-      
-    if (source2_.get() != NULL)
-    {
-      source2_->Update(plane_);
-    }
-
-    if (source3_.get() != NULL)
-    {
-      source3_->Update(plane_);
-    }
-
-    scene_.FitContent(1024, 768);
-      
-    {
-      OrthancStone::CairoCompositor compositor(scene_, 1024, 768);
-      compositor.Refresh();
-        
-      Orthanc::ImageAccessor accessor;
-      compositor.GetCanvas().GetReadOnlyAccessor(accessor);
-
-      Orthanc::Image tmp(Orthanc::PixelFormat_RGB24, accessor.GetWidth(), accessor.GetHeight(), false);
-      Orthanc::ImageProcessing::Convert(tmp, accessor);
-        
-      static unsigned int count = 0;
-      char buf[64];
-      sprintf(buf, "scene-%06d.png", count++);
-        
-      Orthanc::PngWriter writer;
-      writer.WriteToFile(buf, tmp);
-    }
-  }
-
-  
-  void Handle(const OrthancStone::DicomVolumeImage::GeometryReadyMessage& message)
-  {
-    printf("Geometry ready\n");
-    
-    plane_ = message.GetOrigin().GetGeometry().GetSagittalGeometry();
-    //plane_ = message.GetOrigin().GetGeometry().GetAxialGeometry();
-    //plane_ = message.GetOrigin().GetGeometry().GetCoronalGeometry();
-    plane_.SetOrigin(message.GetOrigin().GetGeometry().GetCoordinates(0.5f, 0.5f, 0.5f));
-
-    Refresh();
-  }
-  
-  
-  void Handle(const OrthancStone::SleepOracleCommand::TimeoutMessage& message)
-  {
-    if (message.GetOrigin().HasPayload())
-    {
-      printf("TIMEOUT! %d\n", dynamic_cast<const Orthanc::SingleValueObject<unsigned int>& >(message.GetOrigin().GetPayload()).GetValue());
-    }
-    else
-    {
-      printf("TIMEOUT\n");
-
-      Refresh();
-
-      /**
-       * The sleep() leads to a crash if the oracle is still running,
-       * while this object is destroyed. Always stop the oracle before
-       * destroying active objects.  (*)
-       **/
-      // boost::this_thread::sleep(boost::posix_time::seconds(2));
-
-      oracle_.Schedule(*this, new OrthancStone::SleepOracleCommand(message.GetOrigin().GetDelay()));
-    }
-  }
-
-  void Handle(const OrthancStone::OrthancRestApiCommand::SuccessMessage& message)
-  {
-    Json::Value v;
-    message.ParseJsonBody(v);
-
-    printf("ICI [%s]\n", v.toStyledString().c_str());
-  }
-
-  void Handle(const OrthancStone::GetOrthancImageCommand::SuccessMessage& message)
-  {
-    printf("IMAGE %dx%d\n", message.GetImage().GetWidth(), message.GetImage().GetHeight());
-  }
-
-  void Handle(const OrthancStone::GetOrthancWebViewerJpegCommand::SuccessMessage& message)
-  {
-    printf("WebViewer %dx%d\n", message.GetImage().GetWidth(), message.GetImage().GetHeight());
-  }
-
-  void Handle(const OrthancStone::OracleCommandExceptionMessage& message)
-  {
-    printf("EXCEPTION: [%s] on command type %d\n", message.GetException().What(), message.GetCommand().GetType());
-
-    switch (message.GetCommand().GetType())
-    {
-      case OrthancStone::IOracleCommand::Type_GetOrthancWebViewerJpeg:
-        printf("URI: [%s]\n", dynamic_cast<const OrthancStone::GetOrthancWebViewerJpegCommand&>
-               (message.GetCommand()).GetUri().c_str());
-        break;
-      
-      default:
-        break;
-    }
-  }
-
-public:
-  Toto(OrthancStone::IOracle& oracle,
-       OrthancStone::IObservable& oracleObservable) :
-    IObserver(oracleObservable.GetBroker()),
-    oracle_(oracle)
-  {
-    oracleObservable.RegisterObserverCallback
-      (new OrthancStone::Callable
-       <Toto, OrthancStone::SleepOracleCommand::TimeoutMessage>(*this, &Toto::Handle));
-
-    oracleObservable.RegisterObserverCallback
-      (new OrthancStone::Callable
-       <Toto, OrthancStone::OrthancRestApiCommand::SuccessMessage>(*this, &Toto::Handle));
-
-    oracleObservable.RegisterObserverCallback
-      (new OrthancStone::Callable
-       <Toto, OrthancStone::GetOrthancImageCommand::SuccessMessage>(*this, &Toto::Handle));
-
-    oracleObservable.RegisterObserverCallback
-      (new OrthancStone::Callable
-       <Toto, OrthancStone::GetOrthancWebViewerJpegCommand::SuccessMessage>(*this, &Toto::Handle));
-
-    oracleObservable.RegisterObserverCallback
-      (new OrthancStone::Callable
-       <Toto, OrthancStone::OracleCommandExceptionMessage>(*this, &Toto::Handle));
-  }
-
-  void SetReferenceLoader(OrthancStone::IObservable& loader)
-  {
-    loader.RegisterObserverCallback
-      (new OrthancStone::Callable
-       <Toto, OrthancStone::DicomVolumeImage::GeometryReadyMessage>(*this, &Toto::Handle));
-  }
-
-  void SetVolume1(int depth,
-                  const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume,
-                  OrthancStone::ILayerStyleConfigurator* style)
-  {
-    source1_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume));
-
-    if (style != NULL)
-    {
-      source1_->SetConfigurator(style);
-    }
-  }
-
-  void SetVolume2(int depth,
-                  const boost::shared_ptr<OrthancStone::IVolumeSlicer>& volume,
-                  OrthancStone::ILayerStyleConfigurator* style)
-  {
-    source2_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume));
-
-    if (style != NULL)
-    {
-      source2_->SetConfigurator(style);
-    }
-  }
-
-  void SetStructureSet(int depth,
-                       const boost::shared_ptr<OrthancStone::DicomStructureSetLoader>& volume)
-  {
-    source3_.reset(new OrthancStone::VolumeSceneLayerSource(scene_, depth, volume));
-  }
-                       
-};
-
-
-void Run(OrthancStone::NativeApplicationContext& context,
-         OrthancStone::ThreadedOracle& oracle)
-{
-  // the oracle has been supplied with the context (as an IEmitter) upon
-  // creation
-  boost::shared_ptr<OrthancStone::DicomVolumeImage>  ct(new OrthancStone::DicomVolumeImage);
-  boost::shared_ptr<OrthancStone::DicomVolumeImage>  dose(new OrthancStone::DicomVolumeImage);
-
-  
-  boost::shared_ptr<Toto> toto;
-  boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader> ctLoader;
-  boost::shared_ptr<OrthancStone::OrthancMultiframeVolumeLoader> doseLoader;
-  boost::shared_ptr<OrthancStone::DicomStructureSetLoader>  rtstructLoader;
-
-  {
-    OrthancStone::NativeApplicationContext::WriterLock lock(context);
-    toto.reset(new Toto(oracle, lock.GetOracleObservable()));
-
-    // the oracle is used to schedule commands
-    // the oracleObservable is used by the loaders to:
-    // - request the broker (lifetime mgmt)
-    // - register the loader callbacks (called indirectly by the oracle)
-    ctLoader.reset(new OrthancStone::OrthancSeriesVolumeProgressiveLoader(ct, oracle, lock.GetOracleObservable()));
-    doseLoader.reset(new OrthancStone::OrthancMultiframeVolumeLoader(dose, oracle, lock.GetOracleObservable()));
-    rtstructLoader.reset(new OrthancStone::DicomStructureSetLoader(oracle, lock.GetOracleObservable()));
-  }
-
-
-  //toto->SetReferenceLoader(*ctLoader);
-  toto->SetReferenceLoader(*doseLoader);
-
-
-#if 1
-  toto->SetVolume1(0, ctLoader, new OrthancStone::GrayscaleStyleConfigurator);
-#else
-  {
-    boost::shared_ptr<OrthancStone::IVolumeSlicer> reslicer(new OrthancStone::DicomVolumeImageReslicer(ct));
-    toto->SetVolume1(0, reslicer, new OrthancStone::GrayscaleStyleConfigurator);
-  }
-#endif  
-  
-
-  {
-    std::unique_ptr<OrthancStone::LookupTableStyleConfigurator> config(new OrthancStone::LookupTableStyleConfigurator);
-    config->SetLookupTable(Orthanc::EmbeddedResources::COLORMAP_HOT);
-
-    boost::shared_ptr<OrthancStone::DicomVolumeImageMPRSlicer> tmp(new OrthancStone::DicomVolumeImageMPRSlicer(dose));
-    toto->SetVolume2(1, tmp, config.release());
-  }
-
-  toto->SetStructureSet(2, rtstructLoader);
-  
-  oracle.Schedule(*toto, new OrthancStone::SleepOracleCommand(100));
-
-  if (0)
-  {
-    Json::Value v = Json::objectValue;
-    v["Level"] = "Series";
-    v["Query"] = Json::objectValue;
-
-    std::unique_ptr<OrthancStone::OrthancRestApiCommand>  command(new OrthancStone::OrthancRestApiCommand);
-    command->SetMethod(Orthanc::HttpMethod_Post);
-    command->SetUri("/tools/find");
-    command->SetBody(v);
-
-    oracle.Schedule(*toto, command.release());
-  }
-  
-  if(0)
-  {
-    if (0)
-    {
-      std::unique_ptr<OrthancStone::GetOrthancImageCommand>  command(new OrthancStone::GetOrthancImageCommand);
-      command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Jpeg)));
-      command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/preview");
-      oracle.Schedule(*toto, command.release());
-    }
-    
-    if (0)
-    {
-      std::unique_ptr<OrthancStone::GetOrthancImageCommand>  command(new OrthancStone::GetOrthancImageCommand);
-      command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Png)));
-      command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/preview");
-      oracle.Schedule(*toto, command.release());
-    }
-    
-    if (0)
-    {
-      std::unique_ptr<OrthancStone::GetOrthancImageCommand>  command(new OrthancStone::GetOrthancImageCommand);
-      command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Png)));
-      command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16");
-      oracle.Schedule(*toto, command.release());
-    }
-    
-    if (0)
-    {
-      std::unique_ptr<OrthancStone::GetOrthancImageCommand>  command(new OrthancStone::GetOrthancImageCommand);
-      command->SetHttpHeader("Accept-Encoding", "gzip");
-      command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Pam)));
-      command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16");
-      oracle.Schedule(*toto, command.release());
-    }
-    
-    if (0)
-    {
-      std::unique_ptr<OrthancStone::GetOrthancImageCommand>  command(new OrthancStone::GetOrthancImageCommand);
-      command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Pam)));
-      command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16");
-      oracle.Schedule(*toto, command.release());
-    }
-
-    if (0)
-    {
-      std::unique_ptr<OrthancStone::GetOrthancWebViewerJpegCommand>  command(new OrthancStone::GetOrthancWebViewerJpegCommand);
-      command->SetHttpHeader("Accept-Encoding", "gzip");
-      command->SetInstance("e6c7c20b-c9f65d7e-0d76f2e2-830186f2-3e3c600e");
-      command->SetQuality(90);
-      oracle.Schedule(*toto, command.release());
-    }
-
-
-    if (0)
-    {
-      for (unsigned int i = 0; i < 10; i++)
-      {
-        std::unique_ptr<OrthancStone::SleepOracleCommand> command(new OrthancStone::SleepOracleCommand(i * 1000));
-        command->SetPayload(new Orthanc::SingleValueObject<unsigned int>(42 * i));
-        oracle.Schedule(*toto, command.release());
-      }
-    }
-  }
-  
-  // 2017-11-17-Anonymized
-#if 0
-  // BGO data
-  ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa");  // CT
-  doseLoader->LoadInstance("830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb");  // RT-DOSE
-  //rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9");  // RT-STRUCT
-#else
-  //ctLoader->LoadSeries("cb3ea4d1-d08f3856-ad7b6314-74d88d77-60b05618");  // CT
-  //doseLoader->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6");  // RT-DOSE
-  //rtstructLoader->LoadInstance("83d9c0c3-913a7fee-610097d7-cbf0522d-fd75bee6");  // RT-STRUCT
-
-  // 2017-05-16
-  ctLoader->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa");  // CT
-  doseLoader->LoadInstance("eac822ef-a395f94e-e8121fe0-8411fef8-1f7bffad");  // RT-DOSE
-  rtstructLoader->LoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9");  // RT-STRUCT
-#endif
-  // 2015-01-28-Multiframe
-  //doseLoader->LoadInstance("88f71e2a-5fad1c61-96ed14d6-5b3d3cf7-a5825279");  // Multiframe CT
-  
-  // Delphine
-  //ctLoader->LoadSeries("5990e39c-51e5f201-fe87a54c-31a55943-e59ef80e");  // CT
-  //ctLoader->LoadSeries("67f1b334-02c16752-45026e40-a5b60b6b-030ecab5");  // Lung 1/10mm
-
-
-  {
-    LOG(WARNING) << "...Waiting for Ctrl-C...";
-
-    oracle.Start();
-
-    Orthanc::SystemToolbox::ServerBarrier();
-
-    /**
-     * WARNING => The oracle must be stopped BEFORE the objects using
-     * it are destroyed!!! This forces to wait for the completion of
-     * the running callback methods. Otherwise, the callbacks methods
-     * might still be running while their parent object is destroyed,
-     * resulting in crashes. This is very visible if adding a sleep(),
-     * as in (*).
-     **/
-
-    oracle.Stop();
-  }
-}
-
-
-
-/**
- * IMPORTANT: The full arguments to "main()" are needed for SDL on
- * Windows. Otherwise, one gets the linking error "undefined reference
- * to `SDL_main'". https://wiki.libsdl.org/FAQWindows
- **/
-int main(int argc, char* argv[])
-{
-  OrthancStone::StoneInitialize();
-  //Orthanc::Logging::EnableInfoLevel(true);
-
-  try
-  {
-    OrthancStone::NativeApplicationContext context;
-
-    OrthancStone::ThreadedOracle oracle(context);
-    //oracle.SetThreadsCount(1);
-
-    {
-      Orthanc::WebServiceParameters p;
-      //p.SetUrl("http://localhost:8043/");
-      p.SetCredentials("orthanc", "orthanc");
-      oracle.SetOrthancParameters(p);
-    }
-
-    //oracle.Start();
-
-    Run(context, oracle);
-    
-    //oracle.Stop();
-  }
-  catch (Orthanc::OrthancException& e)
-  {
-    LOG(ERROR) << "EXCEPTION: " << e.What();
-  }
-
-  OrthancStone::StoneFinalize();
-
-  return 0;
-}
--- a/Samples/Sdl/RadiographyEditor.cpp	Tue Apr 07 14:29:01 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,267 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 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 "../Shared/RadiographyEditorApp.h"
-
-// From Stone
-#include "../../Framework/Oracle/SleepOracleCommand.h"
-#include "../../Framework/Oracle/ThreadedOracle.h"
-#include "../../Applications/Sdl/SdlOpenGLWindow.h"
-#include "../../Framework/Scene2D/OpenGLCompositor.h"
-#include "../../Framework/Scene2D/CairoCompositor.h"
-#include "../../Framework/Scene2D/ColorTextureSceneLayer.h"
-#include "../../Framework/Scene2D/OpenGLCompositor.h"
-#include "../../Framework/StoneInitialization.h"
-
-#include <Core/Logging.h>
-#include <Core/OrthancException.h>
-
-
-#include <boost/shared_ptr.hpp>
-#include <boost/weak_ptr.hpp>
-
-#include <SDL.h>
-#include <stdio.h>
-
-using namespace OrthancStone;
-
-namespace OrthancStone
-{
-  class NativeApplicationContext : public IMessageEmitter
-  {
-  private:
-    boost::shared_mutex  mutex_;
-    MessageBroker        broker_;
-    IObservable          oracleObservable_;
-
-  public:
-    NativeApplicationContext() :
-      oracleObservable_(broker_)
-    {
-    }
-
-
-    virtual void EmitMessage(const IObserver& observer,
-                             const IMessage& message) ORTHANC_OVERRIDE
-    {
-      try
-      {
-        boost::unique_lock<boost::shared_mutex>  lock(mutex_);
-        oracleObservable_.EmitMessage(observer, message);
-      }
-      catch (Orthanc::OrthancException& e)
-      {
-        LOG(ERROR) << "Exception while emitting a message: " << e.What();
-      }
-    }
-
-
-    class ReaderLock : public boost::noncopyable
-    {
-    private:
-      NativeApplicationContext&                that_;
-      boost::shared_lock<boost::shared_mutex>  lock_;
-
-    public:
-      ReaderLock(NativeApplicationContext& that) :
-        that_(that),
-        lock_(that.mutex_)
-      {
-      }
-    };
-
-
-    class WriterLock : public boost::noncopyable
-    {
-    private:
-      NativeApplicationContext&                that_;
-      boost::unique_lock<boost::shared_mutex>  lock_;
-
-    public:
-      WriterLock(NativeApplicationContext& that) :
-        that_(that),
-        lock_(that.mutex_)
-      {
-      }
-
-      MessageBroker& GetBroker()
-      {
-        return that_.broker_;
-      }
-
-      IObservable& GetOracleObservable()
-      {
-        return that_.oracleObservable_;
-      }
-    };
-  };
-}
-
-class OpenGlSdlCompositorFactory : public ICompositorFactory
-{
-  OpenGL::IOpenGLContext& openGlContext_;
-
-public:
-  OpenGlSdlCompositorFactory(OpenGL::IOpenGLContext& openGlContext) :
-    openGlContext_(openGlContext)
-  {}
-
-  ICompositor* GetCompositor(const Scene2D& scene)
-  {
-
-    OpenGLCompositor* compositor = new OpenGLCompositor(openGlContext_, scene);
-    compositor->SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT,
-                         FONT_SIZE_0, Orthanc::Encoding_Latin1);
-    compositor->SetFont(1, Orthanc::EmbeddedResources::UBUNTU_FONT,
-                         FONT_SIZE_1, Orthanc::Encoding_Latin1);
-    return compositor;
-  }
-};
-
-static void GLAPIENTRY
-OpenGLMessageCallback(GLenum source,
-                      GLenum type,
-                      GLuint id,
-                      GLenum severity,
-                      GLsizei length,
-                      const GLchar* message,
-                      const void* userParam)
-{
-  if (severity != GL_DEBUG_SEVERITY_NOTIFICATION)
-  {
-    fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
-            (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""),
-            type, severity, message);
-  }
-}
-
-
-/**
- * IMPORTANT: The full arguments to "main()" are needed for SDL on
- * Windows. Otherwise, one gets the linking error "undefined reference
- * to `SDL_main'". https://wiki.libsdl.org/FAQWindows
- **/
-int main(int argc, char* argv[])
-{
-  using namespace OrthancStone;
-
-  StoneInitialize();
-  Orthanc::Logging::EnableInfoLevel(true);
-  //  Orthanc::Logging::EnableTraceLevel(true);
-
-  try
-  {
-    OrthancStone::NativeApplicationContext context;
-    OrthancStone::NativeApplicationContext::WriterLock lock(context);
-    OrthancStone::ThreadedOracle oracle(context);
-
-    // False means we do NOT let Windows treat this as a legacy application
-    // that needs to be scaled
-    SdlOpenGLWindow window("Hello", 1024, 1024, false);
-
-    glEnable(GL_DEBUG_OUTPUT);
-    glDebugMessageCallback(OpenGLMessageCallback, 0);
-
-    std::unique_ptr<OpenGlSdlCompositorFactory> compositorFactory(new OpenGlSdlCompositorFactory(window));
-    boost::shared_ptr<RadiographyEditorApp> app(new RadiographyEditorApp(oracle, lock.GetOracleObservable(), compositorFactory.release()));
-    app->PrepareScene();
-    app->FitContent(window.GetCanvasWidth(), window.GetCanvasHeight());
-
-    bool stopApplication = false;
-
-    while (!stopApplication)
-    {
-      app->Refresh();
-
-      SDL_Event event;
-      while (!stopApplication && SDL_PollEvent(&event))
-      {
-        OrthancStone::KeyboardModifiers modifiers = OrthancStone::KeyboardModifiers_None;
-        if (event.key.keysym.mod & KMOD_CTRL)
-          modifiers = static_cast<OrthancStone::KeyboardModifiers>(static_cast<int>(modifiers) | static_cast<int>(OrthancStone::KeyboardModifiers_Control));
-        if (event.key.keysym.mod & KMOD_ALT)
-          modifiers = static_cast<OrthancStone::KeyboardModifiers>(static_cast<int>(modifiers) | static_cast<int>(OrthancStone::KeyboardModifiers_Alt));
-        if (event.key.keysym.mod & KMOD_SHIFT)
-          modifiers = static_cast<OrthancStone::KeyboardModifiers>(static_cast<int>(modifiers) | static_cast<int>(OrthancStone::KeyboardModifiers_Shift));
-
-        OrthancStone::MouseButton button;
-        if (event.button.button == SDL_BUTTON_LEFT)
-          button = OrthancStone::MouseButton_Left;
-        else if (event.button.button == SDL_BUTTON_MIDDLE)
-          button = OrthancStone::MouseButton_Middle;
-        else if (event.button.button == SDL_BUTTON_RIGHT)
-          button = OrthancStone::MouseButton_Right;
-
-        if (event.type == SDL_QUIT)
-        {
-          stopApplication = true;
-          break;
-        }
-        else if (event.type == SDL_WINDOWEVENT &&
-                 event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
-        {
-          app->DisableTracker(); // was: tracker.reset(NULL);
-          app->UpdateSize();
-        }
-        else if (event.type == SDL_KEYDOWN &&
-                 event.key.repeat == 0 /* Ignore key bounce */)
-        {
-          switch (event.key.keysym.sym)
-          {
-          case SDLK_f:
-            window.GetWindow().ToggleMaximize();
-            break;
-
-          case SDLK_q:
-            stopApplication = true;
-            break;
-          default:
-          {
-            app->OnKeyPressed(event.key.keysym.sym, modifiers);
-           }
-          }
-        }
-        else if (event.type == SDL_MOUSEBUTTONDOWN)
-        {
-          app->OnMouseDown(event.button.x, event.button.y, modifiers, button);
-        }
-        else if (event.type == SDL_MOUSEMOTION)
-        {
-          app->OnMouseMove(event.button.x, event.button.y, modifiers);
-        }
-        else if (event.type == SDL_MOUSEBUTTONUP)
-        {
-          app->OnMouseUp(event.button.x, event.button.y, modifiers, button);
-        }
-      }
-      SDL_Delay(1);
-    }
-  }
-  catch (Orthanc::OrthancException& e)
-  {
-    LOG(ERROR) << "EXCEPTION: " << e.What();
-  }
-
-  StoneFinalize();
-
-  return 0;
-}
-
-
--- a/Samples/Sdl/TrackerSample.cpp	Tue Apr 07 14:29:01 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,93 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 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 "TrackerSampleApp.h"
-
- // From Stone
-#include "../../Framework/OpenGL/SdlOpenGLContext.h"
-#include "../../Framework/Scene2D/CairoCompositor.h"
-#include "../../Framework/Scene2D/ColorTextureSceneLayer.h"
-#include "../../Framework/Scene2D/OpenGLCompositor.h"
-#include "../../Framework/StoneInitialization.h"
-
-#include <Core/Logging.h>
-#include <Core/OrthancException.h>
-
-
-#include <boost/shared_ptr.hpp>
-#include <boost/weak_ptr.hpp>
-
-#include <SDL.h>
-#include <stdio.h>
-
-/*
-TODO:
-
-- to decouple the trackers from the sample, we need to supply them with
-  the scene rather than the app
-
-- in order to do that, we need a GetNextFreeZIndex function (or something 
-  along those lines) in the scene object
-
-*/
-
-boost::weak_ptr<OrthancStone::TrackerSampleApp> g_app;
-
-void TrackerSample_SetInfoDisplayMessage(std::string key, std::string value)
-{
-  boost::shared_ptr<OrthancStone::TrackerSampleApp> app = g_app.lock();
-  if (app)
-  {
-    app->SetInfoDisplayMessage(key, value);
-  }
-}
-
-/**
- * IMPORTANT: The full arguments to "main()" are needed for SDL on
- * Windows. Otherwise, one gets the linking error "undefined reference
- * to `SDL_main'". https://wiki.libsdl.org/FAQWindows
- **/
-int main(int argc, char* argv[])
-{
-  using namespace OrthancStone;
-
-  StoneInitialize();
-  Orthanc::Logging::EnableInfoLevel(true);
-//  Orthanc::Logging::EnableTraceLevel(true);
-
-  try
-  {
-    MessageBroker broker;
-    boost::shared_ptr<TrackerSampleApp> app(new TrackerSampleApp(broker));
-    g_app = app;
-    app->PrepareScene();
-    app->Run();
-  }
-  catch (Orthanc::OrthancException& e)
-  {
-    LOG(ERROR) << "EXCEPTION: " << e.What();
-  }
-
-  StoneFinalize();
-
-  return 0;
-}
-
-
--- a/Samples/Sdl/TrackerSampleApp.cpp	Tue Apr 07 14:29:01 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,733 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 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 "TrackerSampleApp.h"
-
-#include "../../Framework/OpenGL/SdlOpenGLContext.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/Scene2DViewport/UndoStack.h"
-#include "../../Framework/Scene2DViewport/CreateAngleMeasureTracker.h"
-#include "../../Framework/Scene2DViewport/CreateLineMeasureTracker.h"
-#include "../../Framework/StoneInitialization.h"
-
-// From Orthanc framework
-#include <Core/Logging.h>
-#include <Core/OrthancException.h>
-#include <Core/Images/Image.h>
-#include <Core/Images/ImageProcessing.h>
-#include <Core/Images/PngWriter.h>
-
-#include <boost/ref.hpp>
-#include <boost/make_shared.hpp>
-#include <SDL.h>
-
-#include <stdio.h>
-
-namespace OrthancStone
-{
-  const char* MeasureToolToString(size_t i)
-  {
-    static const char* descs[] = {
-      "GuiTool_Rotate",
-      "GuiTool_Pan",
-      "GuiTool_Zoom",
-      "GuiTool_LineMeasure",
-      "GuiTool_CircleMeasure",
-      "GuiTool_AngleMeasure",
-      "GuiTool_EllipseMeasure",
-      "GuiTool_LAST"
-    };
-    if (i >= GuiTool_LAST)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Wrong tool index");
-    }
-    return descs[i];
-  }
-
-  void TrackerSampleApp::SelectNextTool()
-  {
-    currentTool_ = static_cast<GuiTool>(currentTool_ + 1);
-    if (currentTool_ == GuiTool_LAST)
-      currentTool_ = static_cast<GuiTool>(0);;
-    printf("Current tool is now: %s\n", MeasureToolToString(currentTool_));
-  }
-
-  void TrackerSampleApp::DisplayInfoText()
-  {
-    // do not try to use stuff too early!
-    std::stringstream msg;
-	
-	for (std::map<std::string, std::string>::const_iterator kv = infoTextMap_.begin();
-		kv != infoTextMap_.end(); ++kv)
-    {
-      msg << kv->first << " : " << kv->second << std::endl;
-    }
-	std::string msgS = msg.str();
-
-    TextSceneLayer* layerP = NULL;
-    if (controller_->GetScene().HasLayer(FIXED_INFOTEXT_LAYER_ZINDEX))
-    {
-      TextSceneLayer& layer = dynamic_cast<TextSceneLayer&>(
-        controller_->GetScene().GetLayer(FIXED_INFOTEXT_LAYER_ZINDEX));
-      layerP = &layer;
-    }
-    else
-    {
-      std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer);
-      layerP = layer.get();
-      layer->SetColor(0, 255, 0);
-      layer->SetFontIndex(1);
-      layer->SetBorder(20);
-      layer->SetAnchor(BitmapAnchor_TopLeft);
-      //layer->SetPosition(0,0);
-      controller_->GetScene().SetLayer(FIXED_INFOTEXT_LAYER_ZINDEX, layer.release());
-    }
-    // position the fixed info text in the upper right corner
-    layerP->SetText(msgS.c_str());
-    double cX = GetCompositor().GetCanvasWidth() * (-0.5);
-    double cY = GetCompositor().GetCanvasHeight() * (-0.5);
-    controller_->GetScene().GetCanvasToSceneTransform().Apply(cX,cY);
-    layerP->SetPosition(cX, cY);
-  }
-
-  void TrackerSampleApp::DisplayFloatingCtrlInfoText(const PointerEvent& e)
-  {
-    ScenePoint2D p = e.GetMainPosition().Apply(controller_->GetScene().GetCanvasToSceneTransform());
-
-    char buf[128];
-    sprintf(buf, "S:(%0.02f,%0.02f) C:(%0.02f,%0.02f)", 
-      p.GetX(), p.GetY(), 
-      e.GetMainPosition().GetX(), e.GetMainPosition().GetY());
-
-    if (controller_->GetScene().HasLayer(FLOATING_INFOTEXT_LAYER_ZINDEX))
-    {
-      TextSceneLayer& layer =
-        dynamic_cast<TextSceneLayer&>(controller_->GetScene().GetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX));
-      layer.SetText(buf);
-      layer.SetPosition(p.GetX(), p.GetY());
-    }
-    else
-    {
-      std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer);
-      layer->SetColor(0, 255, 0);
-      layer->SetText(buf);
-      layer->SetBorder(20);
-      layer->SetAnchor(BitmapAnchor_BottomCenter);
-      layer->SetPosition(p.GetX(), p.GetY());
-      controller_->GetScene().SetLayer(FLOATING_INFOTEXT_LAYER_ZINDEX, layer.release());
-    }
-  }
-
-  void TrackerSampleApp::HideInfoText()
-  {
-    controller_->GetScene().DeleteLayer(FLOATING_INFOTEXT_LAYER_ZINDEX);
-  }
-
-  ScenePoint2D TrackerSampleApp::GetRandomPointInScene() const
-  {
-    unsigned int w = GetCompositor().GetCanvasWidth();
-    LOG(TRACE) << "GetCompositor().GetCanvasWidth() = " << 
-      GetCompositor().GetCanvasWidth();
-    unsigned int h = GetCompositor().GetCanvasHeight();
-    LOG(TRACE) << "GetCompositor().GetCanvasHeight() = " << 
-      GetCompositor().GetCanvasHeight();
-
-    if ((w >= RAND_MAX) || (h >= RAND_MAX))
-      LOG(WARNING) << "Canvas is too big : tools will not be randomly placed";
-
-    int x = rand() % w;
-    int y = rand() % h;
-    LOG(TRACE) << "random x = " << x << "random y = " << y;
-
-    ScenePoint2D p = controller_->GetViewport().GetPixelCenterCoordinates(x, y);
-    LOG(TRACE) << "--> p.GetX() = " << p.GetX() << " p.GetY() = " << p.GetY();
-
-    ScenePoint2D r = p.Apply(controller_->GetScene().GetCanvasToSceneTransform());
-    LOG(TRACE) << "--> r.GetX() = " << r.GetX() << " r.GetY() = " << r.GetY();
-    return r;
-  }
-
-  void TrackerSampleApp::CreateRandomMeasureTool()
-  {
-    static bool srandCalled = false;
-    if (!srandCalled)
-    {
-      srand(42);
-      srandCalled = true;
-    }
-
-    int i = rand() % 2;
-    LOG(TRACE) << "random i = " << i;
-    switch (i)
-    {
-    case 0:
-      // line measure
-      {
-        boost::shared_ptr<CreateLineMeasureCommand> cmd = 
-          boost::make_shared<CreateLineMeasureCommand>(
-		  boost::ref(IObserver::GetBroker()),
-            controller_,
-            GetRandomPointInScene());
-        cmd->SetEnd(GetRandomPointInScene());
-        controller_->PushCommand(cmd);
-      }
-      break;
-    case 1:
-      // angle measure
-      {
-      boost::shared_ptr<CreateAngleMeasureCommand> cmd =
-        boost::make_shared<CreateAngleMeasureCommand>(
-          boost::ref(IObserver::GetBroker()),
-          controller_,
-          GetRandomPointInScene());
-        cmd->SetCenter(GetRandomPointInScene());
-        cmd->SetSide2End(GetRandomPointInScene());
-        controller_->PushCommand(cmd);
-      }
-      break;
-    }
-  }
-
-  void TrackerSampleApp::HandleApplicationEvent(
-    const SDL_Event & event)
-  {
-    DisplayInfoText();
-
-    if (event.type == SDL_MOUSEMOTION)
-    {
-      int scancodeCount = 0;
-      const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount);
-
-      if (activeTracker_.get() == NULL &&
-        SDL_SCANCODE_LALT < scancodeCount &&
-        keyboardState[SDL_SCANCODE_LALT])
-      {
-        // The "left-ctrl" key is down, while no tracker is present
-        // Let's display the info text
-        PointerEvent e;
-        e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(
-          event.button.x, event.button.y));
-
-        DisplayFloatingCtrlInfoText(e);
-      }
-      else if (activeTracker_.get() != NULL)
-      {
-        HideInfoText();
-        //LOG(TRACE) << "(event.type == SDL_MOUSEMOTION)";
-        if (activeTracker_.get() != NULL)
-        {
-          //LOG(TRACE) << "(activeTracker_.get() != NULL)";
-          PointerEvent e;
-          e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(
-            event.button.x, event.button.y));
-          
-          //LOG(TRACE) << "event.button.x = " << event.button.x << "     " <<
-          //  "event.button.y = " << event.button.y;
-          LOG(TRACE) << "activeTracker_->PointerMove(e); " <<
-            e.GetMainPosition().GetX() << " " << e.GetMainPosition().GetY();
-          
-          activeTracker_->PointerMove(e);
-          if (!activeTracker_->IsAlive())
-            activeTracker_.reset();
-        }
-      }
-      else
-      {
-        HideInfoText();
-
-        PointerEvent e;
-        e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(event.button.x, event.button.y));
-
-        ScenePoint2D scenePos = e.GetMainPosition().Apply(
-          controller_->GetScene().GetCanvasToSceneTransform());
-        //auto measureTools = GetController()->HitTestMeasureTools(scenePos);
-        //LOG(TRACE) << "# of hit tests: " << measureTools.size();
-        
-        // this returns the collection of measuring tools where hit test is true
-        std::vector<boost::shared_ptr<MeasureTool> > measureTools = controller_->HitTestMeasureTools(scenePos);
-
-        // let's refresh the measuring tools highlighted state
-        // first let's tag them as "unhighlighted"
-        controller_->ResetMeasuringToolsHighlight();
-
-        // then immediately take the first one and ask it to highlight the 
-        // measuring tool UI part that is hot
-        if (measureTools.size() > 0)
-        {
-          measureTools[0]->Highlight(scenePos);
-        }
-      }
-    }
-    else if (event.type == SDL_MOUSEBUTTONUP)
-    {
-      if (activeTracker_)
-      {
-        PointerEvent e;
-        e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(event.button.x, event.button.y));
-        activeTracker_->PointerUp(e);
-        if (!activeTracker_->IsAlive())
-          activeTracker_.reset();
-      }
-    }
-    else if (event.type == SDL_MOUSEBUTTONDOWN)
-    {
-      PointerEvent e;
-      e.AddPosition(controller_->GetViewport().GetPixelCenterCoordinates(
-        event.button.x, event.button.y));
-      if (activeTracker_)
-      {
-        activeTracker_->PointerDown(e);
-        if (!activeTracker_->IsAlive())
-          activeTracker_.reset();
-      }
-      else
-      {
-        // we ATTEMPT to create a tracker if need be
-        activeTracker_ = CreateSuitableTracker(event, e);
-      }
-    }
-    else if (event.type == SDL_KEYDOWN &&
-      event.key.repeat == 0 /* Ignore key bounce */)
-    {
-      switch (event.key.keysym.sym)
-      {
-      case SDLK_ESCAPE:
-        if (activeTracker_)
-        {
-          activeTracker_->Cancel();
-          if (!activeTracker_->IsAlive())
-            activeTracker_.reset();
-        }
-        break;
-
-      case SDLK_t:
-        if (!activeTracker_)
-          SelectNextTool();
-        else
-        {
-          LOG(WARNING) << "You cannot change the active tool when an interaction"
-            " is taking place";
-        }
-        break;
-
-      case SDLK_m:
-        CreateRandomMeasureTool();
-        break;
-      case SDLK_s:
-        controller_->FitContent(GetCompositor().GetCanvasWidth(),
-          GetCompositor().GetCanvasHeight());
-        break;
-
-      case SDLK_z:
-        LOG(TRACE) << "SDLK_z has been pressed. event.key.keysym.mod == " << event.key.keysym.mod;
-        if (event.key.keysym.mod & KMOD_CTRL)
-        {
-          if (controller_->CanUndo())
-          {
-            LOG(TRACE) << "Undoing...";
-            controller_->Undo();
-          }
-          else
-          {
-            LOG(WARNING) << "Nothing to undo!!!";
-          }
-        }
-        break;
-
-      case SDLK_y:
-        LOG(TRACE) << "SDLK_y has been pressed. event.key.keysym.mod == " << event.key.keysym.mod;
-        if (event.key.keysym.mod & KMOD_CTRL)
-        {
-          if (controller_->CanRedo())
-          {
-            LOG(TRACE) << "Redoing...";
-            controller_->Redo();
-          }
-          else
-          {
-            LOG(WARNING) << "Nothing to redo!!!";
-          }
-        }
-        break;
-
-      case SDLK_c:
-        TakeScreenshot(
-          "screenshot.png",
-          GetCompositor().GetCanvasWidth(),
-          GetCompositor().GetCanvasHeight());
-        break;
-
-      default:
-        break;
-      }
-    }
-  }
-
-
-  void TrackerSampleApp::OnSceneTransformChanged(
-    const ViewportController::SceneTransformChanged& message)
-  {
-    DisplayInfoText();
-  }
-
-  boost::shared_ptr<IFlexiblePointerTracker> TrackerSampleApp::CreateSuitableTracker(
-    const SDL_Event & event,
-    const PointerEvent & e)
-  {
-    using namespace Orthanc;
-
-    switch (event.button.button)
-    {
-    case SDL_BUTTON_MIDDLE:
-      return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker
-        (controller_, e));
-
-    case SDL_BUTTON_RIGHT:
-      return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker
-        (controller_, e, GetCompositor().GetCanvasHeight()));
-
-    case SDL_BUTTON_LEFT:
-    {
-      //LOG(TRACE) << "CreateSuitableTracker: case SDL_BUTTON_LEFT:";
-      // TODO: we need to iterate on the set of measuring tool and perform
-      // a hit test to check if a tracker needs to be created for edition.
-      // Otherwise, depending upon the active tool, we might want to create
-      // a "measuring tool creation" tracker
-
-      // TODO: if there are conflicts, we should prefer a tracker that 
-      // pertains to the type of measuring tool currently selected (TBD?)
-      boost::shared_ptr<IFlexiblePointerTracker> hitTestTracker = TrackerHitTest(e);
-
-      if (hitTestTracker != NULL)
-      {
-        //LOG(TRACE) << "hitTestTracker != NULL";
-        return hitTestTracker;
-      }
-      else
-      {
-        switch (currentTool_)
-        {
-        case GuiTool_Rotate:
-          //LOG(TRACE) << "Creating RotateSceneTracker";
-          return boost::shared_ptr<IFlexiblePointerTracker>(new RotateSceneTracker(
-            controller_, e));
-        case GuiTool_Pan:
-          return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker(
-            controller_, e));
-        case GuiTool_Zoom:
-          return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker(
-            controller_, e, GetCompositor().GetCanvasHeight()));
-        //case GuiTool_AngleMeasure:
-        //  return new AngleMeasureTracker(GetScene(), e);
-        //case GuiTool_CircleMeasure:
-        //  return new CircleMeasureTracker(GetScene(), e);
-        //case GuiTool_EllipseMeasure:
-        //  return new EllipseMeasureTracker(GetScene(), e);
-        case GuiTool_LineMeasure:
-          return boost::shared_ptr<IFlexiblePointerTracker>(new CreateLineMeasureTracker(
-            IObserver::GetBroker(), controller_, e));
-        case GuiTool_AngleMeasure:
-          return boost::shared_ptr<IFlexiblePointerTracker>(new CreateAngleMeasureTracker(
-            IObserver::GetBroker(), controller_, e));
-        case GuiTool_CircleMeasure:
-          LOG(ERROR) << "Not implemented yet!";
-          return boost::shared_ptr<IFlexiblePointerTracker>();
-        case GuiTool_EllipseMeasure:
-          LOG(ERROR) << "Not implemented yet!";
-          return boost::shared_ptr<IFlexiblePointerTracker>();
-        default:
-          throw OrthancException(ErrorCode_InternalError, "Wrong tool!");
-        }
-      }
-    }
-    default:
-      return boost::shared_ptr<IFlexiblePointerTracker>();
-    }
-  }
-
-
-  TrackerSampleApp::TrackerSampleApp(MessageBroker& broker) : IObserver(broker)
-    , currentTool_(GuiTool_Rotate)
-    , undoStack_(new UndoStack)
-    , viewport_("Hello", 1024, 1024, false) // False means we do NOT let Windows treat this as a legacy application that needs to be scaled
-  {
-    controller_ = boost::shared_ptr<ViewportController>(
-      new ViewportController(undoStack_, broker, viewport_));
-
-    controller_->RegisterObserverCallback(
-      new Callable<TrackerSampleApp, ViewportController::SceneTransformChanged>
-      (*this, &TrackerSampleApp::OnSceneTransformChanged));
-
-    TEXTURE_2x2_1_ZINDEX = 1;
-    TEXTURE_1x1_ZINDEX = 2;
-    TEXTURE_2x2_2_ZINDEX = 3;
-    LINESET_1_ZINDEX = 4;
-    LINESET_2_ZINDEX = 5;
-    FLOATING_INFOTEXT_LAYER_ZINDEX = 6;
-    FIXED_INFOTEXT_LAYER_ZINDEX = 7;
-  }
-
-  void TrackerSampleApp::PrepareScene()
-  {
-    // Texture of 2x2 size
-    {
-      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;
-
-      controller_->GetScene().SetLayer(TEXTURE_2x2_1_ZINDEX, new ColorTextureSceneLayer(i));
-
-      std::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i));
-      l->SetOrigin(-3, 2);
-      l->SetPixelSpacing(1.5, 1);
-      l->SetAngle(20.0 / 180.0 * M_PI);
-      controller_->GetScene().SetLayer(TEXTURE_2x2_2_ZINDEX, l.release());
-    }
-
-    // Texture of 1x1 size
-    {
-      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::unique_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i));
-      l->SetOrigin(-2, 1);
-      l->SetAngle(20.0 / 180.0 * M_PI);
-      controller_->GetScene().SetLayer(TEXTURE_1x1_ZINDEX, l.release());
-    }
-
-    // Some lines
-    {
-      std::unique_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, 255, 0, 0);
-
-      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, 0, 255, 0);
-
-      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, 0, 0, 255);
-
-      controller_->GetScene().SetLayer(LINESET_1_ZINDEX, layer.release());
-    }
-
-    // Some text
-    {
-      std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer);
-      layer->SetText("Hello");
-      controller_->GetScene().SetLayer(LINESET_2_ZINDEX, layer.release());
-    }
-  }
-
-
-  void TrackerSampleApp::DisableTracker()
-  {
-    if (activeTracker_)
-    {
-      activeTracker_->Cancel();
-      activeTracker_.reset();
-    }
-  }
-
-  void TrackerSampleApp::TakeScreenshot(const std::string& target,
-    unsigned int canvasWidth,
-    unsigned int canvasHeight)
-  {
-    CairoCompositor compositor(controller_->GetScene(), canvasWidth, canvasHeight);
-    compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, FONT_SIZE_0, Orthanc::Encoding_Latin1);
-    compositor.Refresh();
-
-    Orthanc::ImageAccessor canvas;
-    compositor.GetCanvas().GetReadOnlyAccessor(canvas);
-
-    Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false);
-    Orthanc::ImageProcessing::Convert(png, canvas);
-
-    Orthanc::PngWriter writer;
-    writer.WriteToFile(target, png);
-  }
-
-
-  boost::shared_ptr<IFlexiblePointerTracker> TrackerSampleApp::TrackerHitTest(const PointerEvent & e)
-  {
-    // std::vector<boost::shared_ptr<MeasureTool>> measureTools_;
-    ScenePoint2D scenePos = e.GetMainPosition().Apply(
-      controller_->GetScene().GetCanvasToSceneTransform());
-
-    std::vector<boost::shared_ptr<MeasureTool> > measureTools = controller_->HitTestMeasureTools(scenePos);
-
-    if (measureTools.size() > 0)
-    {
-      return measureTools[0]->CreateEditionTracker(e);
-    }
-    return boost::shared_ptr<IFlexiblePointerTracker>();
-  }
-
-  static void GLAPIENTRY
-    OpenGLMessageCallback(GLenum source,
-      GLenum type,
-      GLuint id,
-      GLenum severity,
-      GLsizei length,
-      const GLchar* message,
-      const void* userParam)
-  {
-    if (severity != GL_DEBUG_SEVERITY_NOTIFICATION)
-    {
-      fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
-        (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""),
-        type, severity, message);
-    }
-  }
-
-  static bool g_stopApplication = false;
-  
-  ICompositor& TrackerSampleApp::GetCompositor()
-  {
-    using namespace Orthanc;
-    try
-    {
-      SdlViewport& viewport = dynamic_cast<SdlViewport&>(viewport_);
-      return viewport.GetCompositor();
-    }
-    catch (std::bad_cast e)
-    {
-      throw OrthancException(ErrorCode_InternalError, "Wrong viewport type!");
-     }
-  }
-
-  const ICompositor& TrackerSampleApp::GetCompositor() const
-  {
-    using namespace Orthanc;
-    try
-    {
-      SdlViewport& viewport = const_cast<SdlViewport&>(dynamic_cast<const SdlViewport&>(viewport_));
-      return viewport.GetCompositor();
-    }
-    catch (std::bad_cast e)
-    {
-      throw OrthancException(ErrorCode_InternalError, "Wrong viewport type!");
-    }
-  }
-
-
-  void TrackerSampleApp::Run()
-  {
-    controller_->FitContent(viewport_.GetCanvasWidth(), viewport_.GetCanvasHeight());
-
-    glEnable(GL_DEBUG_OUTPUT);
-    glDebugMessageCallback(OpenGLMessageCallback, 0);
-
-    GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT,
-      FONT_SIZE_0, Orthanc::Encoding_Latin1);
-    GetCompositor().SetFont(1, Orthanc::EmbeddedResources::UBUNTU_FONT,
-      FONT_SIZE_1, Orthanc::Encoding_Latin1);
-
-    while (!g_stopApplication)
-    {
-      GetCompositor().Refresh();
-
-      SDL_Event event;
-      while (!g_stopApplication && SDL_PollEvent(&event))
-      {
-        if (event.type == SDL_QUIT)
-        {
-          g_stopApplication = true;
-          break;
-        }
-        else if (event.type == SDL_WINDOWEVENT &&
-          event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
-        {
-          DisableTracker(); // was: tracker.reset(NULL);
-        }
-        else if (event.type == SDL_KEYDOWN &&
-          event.key.repeat == 0 /* Ignore key bounce */)
-        {
-          switch (event.key.keysym.sym)
-          {
-          case SDLK_f:
-            viewport_.GetWindow().ToggleMaximize();
-            break;
-
-          case SDLK_q:
-            g_stopApplication = true;
-            break;
-          default:
-            break;
-          }
-        }
-        HandleApplicationEvent(event);
-      }
-      SDL_Delay(1);
-    }
-  }
-
-  void TrackerSampleApp::SetInfoDisplayMessage(
-    std::string key, std::string value)
-  {
-    if (value == "")
-      infoTextMap_.erase(key);
-    else
-      infoTextMap_[key] = value;
-    DisplayInfoText();
-  }
-
-}
--- a/Samples/Sdl/TrackerSampleApp.h	Tue Apr 07 14:29:01 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,148 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 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 "../../Framework/Messages/IObserver.h"
-#include "../../Framework/Scene2D/OpenGLCompositor.h"
-#include "../../Framework/Scene2DViewport/IFlexiblePointerTracker.h"
-#include "../../Framework/Scene2DViewport/MeasureTool.h"
-#include "../../Framework/Scene2DViewport/PredeclaredTypes.h"
-#include "../../Framework/Scene2DViewport/ViewportController.h"
-#include "../../Framework/Viewport/SdlViewport.h"
-
-#include <SDL.h>
-
-#include <boost/make_shared.hpp>
-#include <boost/shared_ptr.hpp>
-#include <boost/enable_shared_from_this.hpp>
-
-namespace OrthancStone
-{
-  enum GuiTool
-  {
-    GuiTool_Rotate = 0,
-    GuiTool_Pan,
-    GuiTool_Zoom,
-    GuiTool_LineMeasure,
-    GuiTool_CircleMeasure,
-    GuiTool_AngleMeasure,
-    GuiTool_EllipseMeasure,
-    GuiTool_LAST
-  };
-
-  const char* MeasureToolToString(size_t i);
-
-  static const unsigned int FONT_SIZE_0 = 32;
-  static const unsigned int FONT_SIZE_1 = 24;
-
-  class Scene2D;
-  class UndoStack;
-
-  class TrackerSampleApp : public IObserver
-    , public boost::enable_shared_from_this<TrackerSampleApp>
-  {
-  public:
-    // 12 because.
-    TrackerSampleApp(MessageBroker& broker);
-    void PrepareScene();
-    void Run();
-    void SetInfoDisplayMessage(std::string key, std::string value);
-    void DisableTracker();
-
-    void HandleApplicationEvent(const SDL_Event& event);
-
-    /**
-    This method is called when the scene transform changes. It allows to
-    recompute the visual elements whose content depend upon the scene transform
-    */
-    void OnSceneTransformChanged(
-      const ViewportController::SceneTransformChanged& message);
-
-  private:
-    void SelectNextTool();
-    void CreateRandomMeasureTool();
-
-
-    /**
-    In the case of this app, the viewport is an SDL viewport and it has 
-    a OpenGLCompositor& GetCompositor() method
-    */
-    ICompositor& GetCompositor();
-
-    /**
-    See the other overload
-    */
-    const ICompositor& GetCompositor() const;
-
-    /**
-    This returns a random point in the canvas part of the scene, but in
-    scene coordinates
-    */
-    ScenePoint2D GetRandomPointInScene() const;
-
-    boost::shared_ptr<IFlexiblePointerTracker> TrackerHitTest(const PointerEvent& e);
-
-    boost::shared_ptr<IFlexiblePointerTracker> CreateSuitableTracker(
-      const SDL_Event& event,
-      const PointerEvent& e);
-
-    void TakeScreenshot(
-      const std::string& target,
-      unsigned int canvasWidth,
-      unsigned int canvasHeight);
-
-    /**
-      This adds the command at the top of the undo stack
-    */
-    void Commit(boost::shared_ptr<TrackerCommand> cmd);
-    void Undo();
-    void Redo();
-
-  private:
-    void DisplayFloatingCtrlInfoText(const PointerEvent& e);
-    void DisplayInfoText();
-    void HideInfoText();
-
-  private:
-    /**
-    WARNING: the measuring tools do store a reference to the scene, and it 
-    paramount that the scene gets destroyed AFTER the measurement tools.
-    */
-    boost::shared_ptr<ViewportController> controller_;
-
-    std::map<std::string, std::string> infoTextMap_;
-    boost::shared_ptr<IFlexiblePointerTracker> activeTracker_;
-
-    //static const int LAYER_POSITION = 150;
-
-    int TEXTURE_2x2_1_ZINDEX;
-    int TEXTURE_1x1_ZINDEX;
-    int TEXTURE_2x2_2_ZINDEX;
-    int LINESET_1_ZINDEX;
-    int LINESET_2_ZINDEX;
-    int FLOATING_INFOTEXT_LAYER_ZINDEX;
-    int FIXED_INFOTEXT_LAYER_ZINDEX;
-
-    GuiTool currentTool_;
-    boost::shared_ptr<UndoStack> undoStack_;
-    SdlOpenGLViewport viewport_;
-  };
-
-}
--- a/Samples/Sdl/cpp.hint	Tue Apr 07 14:29:01 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2 +0,0 @@
-#define ORTHANC_OVERRIDE
-#define ORTHANC_STONE_DEFINE_EMPTY_MESSAGE(FILE, LINE, NAME) class NAME : public ::OrthancStone::IMessage {};
--- a/Samples/WebAssembly/BasicMPR.cpp	Tue Apr 07 14:29:01 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,427 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 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 "dev.h"
-
-#include <emscripten.h>
-
-#include "../../Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.h"
-#include "../../Framework/Oracle/SleepOracleCommand.h"
-#include "../../Framework/Oracle/WebAssemblyOracle.h"
-#include "../../Framework/Scene2D/GrayscaleStyleConfigurator.h"
-#include "../../Framework/StoneInitialization.h"
-#include "../../Framework/Volumes/VolumeSceneLayerSource.h"
-
-
-namespace OrthancStone
-{
-  class VolumeSlicerWidget : public IObserver
-  {
-  private:
-    OrthancStone::WebAssemblyViewport      viewport_;
-    std::unique_ptr<VolumeSceneLayerSource>  source_;
-    VolumeProjection                       projection_;
-    std::vector<CoordinateSystem3D>        planes_;
-    size_t                                 currentPlane_;
-
-    void Handle(const DicomVolumeImage::GeometryReadyMessage& message)
-    {
-      LOG(INFO) << "Geometry is available";
-
-      const VolumeImageGeometry& geometry = message.GetOrigin().GetGeometry();
-
-      const unsigned int depth = geometry.GetProjectionDepth(projection_);
-      currentPlane_ = depth / 2;
-      
-      planes_.resize(depth);
-
-      for (unsigned int z = 0; z < depth; z++)
-      {
-        planes_[z] = geometry.GetProjectionSlice(projection_, z);
-      }
-
-      Refresh();
-
-      viewport_.FitContent();
-    }
-    
-  public:
-    VolumeSlicerWidget(MessageBroker& broker,
-                         const std::string& canvas,
-                         VolumeProjection projection) :
-      IObserver(broker),
-      viewport_(broker, canvas),
-      projection_(projection),
-      currentPlane_(0)
-    {
-    }
-
-    void UpdateSize()
-    {
-      viewport_.UpdateSize();
-    }
-
-    void SetSlicer(int layerDepth,
-                   const boost::shared_ptr<IVolumeSlicer>& slicer,
-                   IObservable& loader,
-                   ILayerStyleConfigurator* configurator)
-    {
-      if (source_.get() != NULL)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls,
-                                        "Only one slicer can be registered");
-      }
-      
-      loader.RegisterObserverCallback(
-        new Callable<VolumeSlicerWidget, DicomVolumeImage::GeometryReadyMessage>
-        (*this, &VolumeSlicerWidget::Handle));
-
-      source_.reset(new VolumeSceneLayerSource(viewport_.GetScene(), layerDepth, slicer));
-
-      if (configurator != NULL)
-      {
-        source_->SetConfigurator(configurator);
-      }
-    }    
-
-    void Refresh()
-    {
-      if (source_.get() != NULL &&
-          currentPlane_ < planes_.size())
-      {
-        source_->Update(planes_[currentPlane_]);
-        viewport_.Refresh();
-      }
-    }
-
-    size_t GetSlicesCount() const
-    {
-      return planes_.size();
-    }
-
-    void Scroll(int delta)
-    {
-      if (!planes_.empty())
-      {
-        int tmp = static_cast<int>(currentPlane_) + delta;
-        unsigned int next;
-
-        if (tmp < 0)
-        {
-          next = 0;
-        }
-        else if (tmp >= static_cast<int>(planes_.size()))
-        {
-          next = planes_.size() - 1;
-        }
-        else
-        {
-          next = static_cast<size_t>(tmp);
-        }
-
-        if (next != currentPlane_)
-        {
-          currentPlane_ = next;
-          Refresh();
-        }
-      }
-    }
-  };
-}
-
-
-
-
-boost::shared_ptr<OrthancStone::DicomVolumeImage>  ct_(new OrthancStone::DicomVolumeImage);
-
-boost::shared_ptr<OrthancStone::OrthancSeriesVolumeProgressiveLoader>  loader_;
-
-std::unique_ptr<OrthancStone::VolumeSlicerWidget>  widget1_;
-std::unique_ptr<OrthancStone::VolumeSlicerWidget>  widget2_;
-std::unique_ptr<OrthancStone::VolumeSlicerWidget>  widget3_;
-
-OrthancStone::MessageBroker  broker_;
-OrthancStone::WebAssemblyOracle  oracle_(broker_);
-
-
-EM_BOOL OnWindowResize(int eventType, const EmscriptenUiEvent *uiEvent, void *userData)
-{
-  try
-  {
-    if (widget1_.get() != NULL)
-    {
-      widget1_->UpdateSize();
-    }
-  
-    if (widget2_.get() != NULL)
-    {
-      widget2_->UpdateSize();
-    }
-  
-    if (widget3_.get() != NULL)
-    {
-      widget3_->UpdateSize();
-    }
-  }
-  catch (Orthanc::OrthancException& e)
-  {
-    LOG(ERROR) << "Exception while updating canvas size: " << e.What();
-  }
-  
-  return true;
-}
-
-
-
-
-EM_BOOL OnAnimationFrame(double time, void *userData)
-{
-  try
-  {
-    if (widget1_.get() != NULL)
-    {
-      widget1_->Refresh();
-    }
-  
-    if (widget2_.get() != NULL)
-    {
-      widget2_->Refresh();
-    }
-  
-    if (widget3_.get() != NULL)
-    {
-      widget3_->Refresh();
-    }
-
-    return true;
-  }
-  catch (Orthanc::OrthancException& e)
-  {
-    LOG(ERROR) << "Exception in the animation loop, stopping now: " << e.What();
-    return false;
-  }  
-}
-
-
-static bool ctrlDown_ = false;
-
-
-EM_BOOL OnMouseWheel(int eventType,
-                     const EmscriptenWheelEvent *wheelEvent,
-                     void *userData)
-{
-  try
-  {
-    if (userData != NULL)
-    {
-      int delta = 0;
-
-      if (wheelEvent->deltaY < 0)
-      {
-        delta = -1;
-      }
-           
-      if (wheelEvent->deltaY > 0)
-      {
-        delta = 1;
-      }
-
-      OrthancStone::VolumeSlicerWidget& widget =
-        *reinterpret_cast<OrthancStone::VolumeSlicerWidget*>(userData);
-      
-      if (ctrlDown_)
-      {
-        delta *= static_cast<int>(widget.GetSlicesCount() / 10);
-      }
-
-      widget.Scroll(delta);
-    }
-  }
-  catch (Orthanc::OrthancException& e)
-  {
-    LOG(ERROR) << "Exception in the wheel event: " << e.What();
-  }
-  
-  return true;
-}
-
-
-EM_BOOL OnKeyDown(int eventType,
-                  const EmscriptenKeyboardEvent *keyEvent,
-                  void *userData)
-{
-  ctrlDown_ = keyEvent->ctrlKey;
-  return false;
-}
-
-
-EM_BOOL OnKeyUp(int eventType,
-                const EmscriptenKeyboardEvent *keyEvent,
-                void *userData)
-{
-  ctrlDown_ = false;
-  return false;
-}
-
-
-
-
-namespace OrthancStone
-{
-  class TestSleep : public IObserver
-  {
-  private:
-    WebAssemblyOracle&  oracle_;
-
-    void Schedule()
-    {
-      oracle_.Schedule(*this, new OrthancStone::SleepOracleCommand(2000));
-    }
-    
-    void Handle(const SleepOracleCommand::TimeoutMessage& message)
-    {
-      LOG(INFO) << "TIMEOUT";
-      Schedule();
-    }
-    
-  public:
-    TestSleep(MessageBroker& broker,
-              WebAssemblyOracle& oracle) :
-      IObserver(broker),
-      oracle_(oracle)
-    {
-      oracle.RegisterObserverCallback(
-        new Callable<TestSleep, SleepOracleCommand::TimeoutMessage>
-        (*this, &TestSleep::Handle));
-
-      LOG(INFO) << "STARTING";
-      Schedule();
-    }
-  };
-
-  //static TestSleep testSleep(broker_, oracle_);
-}
-
-
-
-static std::map<std::string, std::string> arguments_;
-
-static bool GetArgument(std::string& value,
-                        const std::string& key)
-{
-  std::map<std::string, std::string>::const_iterator found = arguments_.find(key);
-
-  if (found == arguments_.end())
-  {
-    return false;
-  }
-  else
-  {
-    value = found->second;
-    return true;
-  }
-}
-
-
-extern "C"
-{
-  int main(int argc, char const *argv[]) 
-  {
-    OrthancStone::StoneInitialize();
-    Orthanc::Logging::EnableInfoLevel(true);
-    // Orthanc::Logging::EnableTraceLevel(true);
-    EM_ASM(window.dispatchEvent(new CustomEvent("WebAssemblyLoaded")););
-  }
-
-  EMSCRIPTEN_KEEPALIVE
-  void SetArgument(const char* key, const char* value)
-  {
-    // This is called for each GET argument (cf. "app.js")
-    LOG(INFO) << "Received GET argument: [" << key << "] = [" << value << "]";
-    arguments_[key] = value;
-  }
-
-  EMSCRIPTEN_KEEPALIVE
-  void Initialize()
-  {
-    try
-    {
-      oracle_.SetOrthancRoot("..");
-      
-      loader_.reset(new OrthancStone::OrthancSeriesVolumeProgressiveLoader(ct_, oracle_, oracle_));
-    
-      widget1_.reset(new OrthancStone::VolumeSlicerWidget(broker_, "mycanvas1", OrthancStone::VolumeProjection_Axial));
-      {
-        std::unique_ptr<OrthancStone::GrayscaleStyleConfigurator> style(new OrthancStone::GrayscaleStyleConfigurator);
-        style->SetLinearInterpolation(true);
-        style->SetWindowing(OrthancStone::ImageWindowing_Bone);
-        widget1_->SetSlicer(0, loader_, *loader_, style.release());
-      }
-      widget1_->UpdateSize();
-
-      widget2_.reset(new OrthancStone::VolumeSlicerWidget(broker_, "mycanvas2", OrthancStone::VolumeProjection_Coronal));
-      {
-        std::unique_ptr<OrthancStone::GrayscaleStyleConfigurator> style(new OrthancStone::GrayscaleStyleConfigurator);
-        style->SetLinearInterpolation(true);
-        style->SetWindowing(OrthancStone::ImageWindowing_Bone);
-        widget2_->SetSlicer(0, loader_, *loader_, style.release());
-      }
-      widget2_->UpdateSize();
-
-      widget3_.reset(new OrthancStone::VolumeSlicerWidget(broker_, "mycanvas3", OrthancStone::VolumeProjection_Sagittal));
-      {
-        std::unique_ptr<OrthancStone::GrayscaleStyleConfigurator> style(new OrthancStone::GrayscaleStyleConfigurator);
-        style->SetLinearInterpolation(true);
-        style->SetWindowing(OrthancStone::ImageWindowing_Bone);
-        widget3_->SetSlicer(0, loader_, *loader_, style.release());
-      }
-      widget3_->UpdateSize();
-
-      emscripten_set_resize_callback("#window", NULL, false, OnWindowResize);
-
-      emscripten_set_wheel_callback("mycanvas1", widget1_.get(), false, OnMouseWheel);
-      emscripten_set_wheel_callback("mycanvas2", widget2_.get(), false, OnMouseWheel);
-      emscripten_set_wheel_callback("mycanvas3", widget3_.get(), false, OnMouseWheel);
-
-      emscripten_set_keydown_callback("#window", NULL, false, OnKeyDown);
-      emscripten_set_keyup_callback("#window", NULL, false, OnKeyUp);
-    
-      emscripten_request_animation_frame_loop(OnAnimationFrame, NULL);
-
-
-      std::string ct;
-      if (GetArgument(ct, "ct"))
-      {
-        //loader_->LoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa");
-        loader_->LoadSeries(ct);
-      }
-      else
-      {
-        LOG(ERROR) << "No Orthanc identifier for the CT series was provided";
-      }
-    }
-    catch (Orthanc::OrthancException& e)
-    {
-      LOG(ERROR) << "Exception during Initialize(): " << e.What();
-    }
-  }
-}
--- a/Samples/WebAssembly/BasicMPR.html	Tue Apr 07 14:29:01 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-<!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" oncontextmenu="return false;"></canvas>
-    <canvas id="mycanvas2" oncontextmenu="return false;"></canvas>
-    <canvas id="mycanvas3" oncontextmenu="return false;"></canvas>
-
-    <script type="text/javascript" src="app.js"></script>
-    <script type="text/javascript" async src="BasicMPR.js"></script>
-  </body>
-</html>
--- a/Samples/WebAssembly/BasicScene.cpp	Tue Apr 07 14:29:01 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,210 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 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 "dev.h"
-
-#include <emscripten.h>
-#include <emscripten/html5.h>
-
-// From Stone
-#include "../../Framework/Scene2D/ColorTextureSceneLayer.h"
-#include "../../Framework/StoneInitialization.h"
-
-// From Orthanc framework
-#include <Core/Images/Image.h>
-#include <Core/Logging.h>
-#include <Core/OrthancException.h>
-
-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::unique_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::unique_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
-  if (1)
-  {
-    std::unique_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, 255, 0, 0);
-
-    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, 0, 255, 0);
-
-    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, 0, 0, 255);
-
-    scene.SetLayer(50, layer.release());
-  }
-
-  // Some text
-  if (1)
-  {
-    std::unique_ptr<TextSceneLayer> layer(new TextSceneLayer);
-    layer->SetText("Hello");
-    scene.SetLayer(100, layer.release());
-  }
-}
-
-
-std::unique_ptr<OrthancStone::WebAssemblyViewport>  viewport1_;
-std::unique_ptr<OrthancStone::WebAssemblyViewport>  viewport2_;
-std::unique_ptr<OrthancStone::WebAssemblyViewport>  viewport3_;
-boost::shared_ptr<OrthancStone::ViewportController>   controller1_;
-boost::shared_ptr<OrthancStone::ViewportController>   controller2_;
-boost::shared_ptr<OrthancStone::ViewportController>   controller3_;
-OrthancStone::MessageBroker broker_;
-
-
-EM_BOOL OnWindowResize(
-  int eventType, const EmscriptenUiEvent *uiEvent, void *userData)
-{
-  if (viewport1_.get() != NULL)
-  {
-    viewport1_->UpdateSize();
-  }
-  
-  if (viewport2_.get() != NULL)
-  {
-    viewport2_->UpdateSize();
-  }
-  
-  if (viewport3_.get() != NULL)
-  {
-    viewport3_->UpdateSize();
-  }
-  
-  return true;
-}
-
-extern "C"
-{
-  int main(int argc, char const *argv[]) 
-  {
-    OrthancStone::StoneInitialize();
-    // Orthanc::Logging::EnableInfoLevel(true);
-    // Orthanc::Logging::EnableTraceLevel(true);
-    EM_ASM(window.dispatchEvent(new CustomEvent("WebAssemblyLoaded")););
-  }
-
-  EMSCRIPTEN_KEEPALIVE
-  void Initialize()
-  {
-    viewport1_.reset(new OrthancStone::WebAssemblyViewport("mycanvas1"));
-    PrepareScene(viewport1_->GetScene());
-    viewport1_->UpdateSize();
-
-    viewport2_.reset(new OrthancStone::WebAssemblyViewport("mycanvas2"));
-    PrepareScene(viewport2_->GetScene());
-    viewport2_->UpdateSize();
-
-    viewport3_.reset(new OrthancStone::WebAssemblyViewport("mycanvas3"));
-    PrepareScene(viewport3_->GetScene());
-    viewport3_->UpdateSize();
-
-    viewport1_->GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, 
-                                        FONT_SIZE, Orthanc::Encoding_Latin1);
-    viewport2_->GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, 
-                                        FONT_SIZE, Orthanc::Encoding_Latin1);
-    viewport3_->GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, 
-                                        FONT_SIZE, Orthanc::Encoding_Latin1);
-
-    controller1_.reset(new OrthancStone::ViewportController(boost::make_shared<OrthancStone::UndoStack>(), broker_, *viewport1_));
-    controller2_.reset(new OrthancStone::ViewportController(boost::make_shared<OrthancStone::UndoStack>(), broker_, *viewport2_));
-    controller3_.reset(new OrthancStone::ViewportController(boost::make_shared<OrthancStone::UndoStack>(), broker_, *viewport3_));
-
-    controller1_->FitContent(viewport1_->GetCanvasWidth(), viewport1_->GetCanvasHeight());
-    controller2_->FitContent(viewport2_->GetCanvasWidth(), viewport2_->GetCanvasHeight());
-    controller3_->FitContent(viewport3_->GetCanvasWidth(), viewport3_->GetCanvasHeight());
-
-    viewport1_->Refresh();
-    viewport2_->Refresh();
-    viewport3_->Refresh();
-
-    SetupEvents("mycanvas1", controller1_);
-    SetupEvents("mycanvas2", controller2_);
-    SetupEvents("mycanvas3", controller3_);
-
-    emscripten_set_resize_callback("#window", NULL, false, OnWindowResize);
-  }
-}
--- a/Samples/WebAssembly/BasicScene.html	Tue Apr 07 14:29:01 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-<!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" oncontextmenu="return false;"></canvas>
-    <canvas id="mycanvas2" oncontextmenu="return false;"></canvas>
-    <canvas id="mycanvas3" oncontextmenu="return false;"></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>
--- a/Samples/WebAssembly/CMakeLists.txt	Tue Apr 07 14:29:01 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,119 +0,0 @@
-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")
-#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXIT_RUNTIME=1")
-
-#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1")
-
-
-#####################################################################
-## 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)
-
-add_definitions(
-  -DORTHANC_ENABLE_LOGGING_PLUGIN=0
-  )
-
-
-#####################################################################
-## Build the samples
-#####################################################################
-
-add_library(OrthancStone STATIC
-  ${ORTHANC_STONE_SOURCES}
-  )
-
-
-if (ON)
-  add_executable(BasicScene
-    BasicScene.cpp
-    #${CMAKE_CURRENT_LIST_DIR}/../Shared/SharedBasicScene.h
-    ${CMAKE_CURRENT_LIST_DIR}/../Shared/SharedBasicScene.cpp
-    )
-
-  target_link_libraries(BasicScene OrthancStone)
-
-  install(
-    TARGETS BasicScene
-    RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}
-    )
-endif()
-
-
-if (ON)
-  add_executable(BasicMPR
-    BasicMPR.cpp
-    )
-
-  target_link_libraries(BasicMPR OrthancStone)
-
-  install(
-    TARGETS BasicMPR
-    RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}
-    )
-endif()
-  
-
-install(
-  FILES
-  ${CMAKE_CURRENT_BINARY_DIR}/BasicMPR.wasm
-  ${CMAKE_CURRENT_BINARY_DIR}/BasicScene.wasm
-  ${CMAKE_SOURCE_DIR}/BasicMPR.html
-  ${CMAKE_SOURCE_DIR}/BasicScene.html
-  ${CMAKE_SOURCE_DIR}/Configuration.json
-  ${CMAKE_SOURCE_DIR}/app.js
-  ${CMAKE_SOURCE_DIR}/index.html
-  DESTINATION ${CMAKE_INSTALL_PREFIX}
-  )
--- a/Samples/WebAssembly/Configuration.json	Tue Apr 07 14:29:01 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-{
-  "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"
-    }
-  }
-}
--- a/Samples/WebAssembly/ConfigurationLocalSJO.json	Tue Apr 07 14:29:01 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +0,0 @@
-{
-  "Plugins": [
-    "/home/jodogne/Subversion/orthanc-webviewer/r/libOrthancWebViewer.so",
-    "/home/jodogne/Subversion/orthanc/r/libServeFolders.so"
-  ],
-  "StorageDirectory" : "/tmp/orthanc-db",
-  "IndexDirectory" : "/tmp/orthanc-db",
-  "RemoteAccessAllowed" : true,
-  "AuthenticationEnabled" : false,
-  "ServeFolders" : {
-    "AllowCache" : false,
-    "GenerateETag" : true,
-    "Folders" : {
-      "/stone" : "/tmp/stone"
-    }
-  },
-  "WebViewer" : {
-    "CachePath" : "/tmp/orthanc-db/WebViewerCache"
-  }
-}
--- a/Samples/WebAssembly/NOTES.txt	Tue Apr 07 14:29:01 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,76 +0,0 @@
-Docker SJO
-==========
-
-$ 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
-$ docker run -p 4242:4242 -p 8042:8042 --rm -v /tmp/stone:/root/stone:ro -v /tmp/stone-db/:/var/lib/orthanc/db/ jodogne/orthanc-plugins:latest /root/stone/Configuration.json --verbose
-
-WARNING: This won't work using "orthanc-plugins:1.5.6", as support for
-PAM is mandatatory in "/instances/.../image-uint16".
-
-
-Docker BGO
-==========
-
-On Ubuntu WSL
--------------
-. ~/apps/emsdk/emsdk_env.sh
-cd /mnt/c/osi/dev/
-mkdir -p build_stone_newsamples_wasm_wsl
-mkdir -p build_install_stone_newsamples_wasm_wsl
-cd build_stone_newsamples_wasm_wsl
-cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON /mnt/c/osi/dev/orthanc-stone/Samples/WebAssembly -DCMAKE_INSTALL_PREFIX=/mnt/c/osi/dev/build_install_stone_newsamples_wasm_wsl
-ninja install
-
-Then, on Windows
------------------
-docker run -p 4242:4242 -p 8042:8042 --rm -v "C:/osi/dev/build_install_stone_newsamples_wasm_wsl:/root/stone:ro" jodogne/orthanc-plugins:1.5.6 /root/stone/Configuration.json --verbose
-
-# WAIT A COUPLE OF SECS
-# if the archive has NOT already been unzipped, unzip it
-# upload dicom files to running orthanc
-
-cd C:\osi\dev\twiga-orthanc-viewer\demo\dicomfiles
-if (-not (test-path RTVIEWER-c8febcc6-eb9e22a4-130f208c-e0a6a4cd-4d432c57)) { unzip RTVIEWER-c8febcc6-eb9e22a4-130f208c-e0a6a4cd-4d432c57.zip}
-ImportDicomFiles.ps1 127.0.0.1 8042 .\RTVIEWER-c8febcc6-eb9e22a4-130f208c-e0a6a4cd-4d432c57\
-
---> localhost:8042 --> Plugins --> serve-folders --> stone --> ...
-
-Local BGO
-==========
-
-. ~/apps/emsdk/emsdk_env.sh
-cd /mnt/c/osi/dev/
-mkdir -p build_stone_newsamples_wasm_wsl
-mkdir -p build_install_stone_newsamples_wasm_wsl
-cd build_stone_newsamples_wasm_wsl
-cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON /mnt/c/osi/dev/orthanc-stone/Samples/WebAssembly -DCMAKE_INSTALL_PREFIX=/mnt/c/osi/dev/build_install_stone_newsamples_wasm_wsl
-
-
-
-TODO: Orthanc.exe 
-
-
-Local SJO
-==========
-
-$ 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
-
-$ make -C ~/Subversion/orthanc/r -j4
-$ make -C ~/Subversion/orthanc-webviewer/r -j4
-$ ~/Subversion/orthanc/r/Orthanc ../ConfigurationLocalSJO.json
-
-
-Local AM
-========
-
-. ~/apps/emsdk/emsdk_env.sh
-cd /mnt/c/o/
-mkdir -p build_stone_newsamples_wasm_wsl
-mkdir -p build_install_stone_newsamples_wasm_wsl
-cd build_stone_newsamples_wasm_wsl
-cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSDK}/fastcomp/emscripten/cmake/Modules/Platform/Emscripten.cmake -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT=/mnt/c/o/orthanc/ -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON /mnt/c/o/orthanc-stone/Samples/WebAssembly -DCMAKE_INSTALL_PREFIX=/mnt/c/o/build_install_stone_newsamples_wasm_wsl
-ninja
--- a/Samples/WebAssembly/app.js	Tue Apr 07 14:29:01 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-/**
- * This is a generic bootstrap code that is shared by all the Stone
- * sample applications.
- **/
-
-// Check support for WebAssembly
-if (!('WebAssembly' in window)) {
-  alert('Sorry, your browser does not support WebAssembly :(');
-} else {
-
-  // Wait for the module to be loaded (the event "WebAssemblyLoaded"
-  // must be emitted by the "main" function)
-  window.addEventListener('WebAssemblyLoaded', function() {
-
-    // Loop over the GET arguments
-    var parameters = window.location.search.substr(1);
-    if (parameters != null && parameters != '') {
-      var tokens = parameters.split('&');
-      for (var i = 0; i < tokens.length; i++) {
-        var arg = tokens[i].split('=');
-        if (arg.length == 2) {
-
-          // Send each GET argument to WebAssembly
-          Module.ccall('SetArgument', null, [ 'string', 'string' ],
-                       [ arg[0], decodeURIComponent(arg[1]) ]);
-        }
-      }
-    }
-
-    // Inform the WebAssembly module that it can start
-    Module.ccall('Initialize', null, null, null);
-  });
-}
--- a/Samples/WebAssembly/dev.h	Tue Apr 07 14:29:01 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,202 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 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
-
-#include "../../Framework/Viewport/WebAssemblyViewport.h"
-#include "../../Framework/Scene2D/OpenGLCompositor.h"
-#include "../../Framework/Scene2D/PanSceneTracker.h"
-#include "../../Framework/Scene2D/RotateSceneTracker.h"
-#include "../../Framework/Scene2D/ZoomSceneTracker.h"
-#include "../../Framework/Scene2DViewport/UndoStack.h"
-#include "../../Framework/Scene2DViewport/ViewportController.h"
-
-#include <Core/OrthancException.h>
-
-#include <emscripten/html5.h>
-#include <boost/make_shared.hpp>
-
-static const unsigned int FONT_SIZE = 32;
-
-namespace OrthancStone
-{
-  class ActiveTracker : public boost::noncopyable
-  {
-  private:
-    boost::shared_ptr<IFlexiblePointerTracker> tracker_;
-    std::string                             canvasIdentifier_;
-    bool                                    insideCanvas_;
-    
-  public:
-    ActiveTracker(const boost::shared_ptr<IFlexiblePointerTracker>& tracker,
-                  const std::string& canvasId) :
-      tracker_(tracker),
-      canvasIdentifier_(canvasId),
-      insideCanvas_(true)
-    {
-      if (tracker_.get() == NULL)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-      }
-    }
-
-    bool IsAlive() const
-    {
-      return tracker_->IsAlive();
-    }
-
-    void PointerMove(const PointerEvent& event)
-    {
-      tracker_->PointerMove(event);
-    }
-
-    void PointerUp(const PointerEvent& event)
-    {
-      tracker_->PointerUp(event);
-    }
-  };
-}
-
-static OrthancStone::PointerEvent* ConvertMouseEvent(
-  const EmscriptenMouseEvent&        source,
-  OrthancStone::IViewport& viewport)
-{
-  std::unique_ptr<OrthancStone::PointerEvent> target(
-    new OrthancStone::PointerEvent);
-
-  target->AddPosition(viewport.GetPixelCenterCoordinates(
-                        source.targetX, source.targetY));
-  target->SetAltModifier(source.altKey);
-  target->SetControlModifier(source.ctrlKey);
-  target->SetShiftModifier(source.shiftKey);
-
-  return target.release();
-}
-
-std::unique_ptr<OrthancStone::ActiveTracker> tracker_;
-
-EM_BOOL OnMouseEvent(int eventType, 
-                     const EmscriptenMouseEvent *mouseEvent, 
-                     void *userData)
-{
-  if (mouseEvent != NULL &&
-      userData != NULL)
-  {
-    boost::shared_ptr<OrthancStone::ViewportController>& controller = 
-      *reinterpret_cast<boost::shared_ptr<OrthancStone::ViewportController>*>(userData);
-
-    switch (eventType)
-    {
-      case EMSCRIPTEN_EVENT_CLICK:
-      {
-        static unsigned int count = 0;
-        char buf[64];
-        sprintf(buf, "click %d", count++);
-
-        std::unique_ptr<OrthancStone::TextSceneLayer> layer(new OrthancStone::TextSceneLayer);
-        layer->SetText(buf);
-        controller->GetViewport().GetScene().SetLayer(100, layer.release());
-        controller->GetViewport().Refresh();
-        break;
-      }
-
-      case EMSCRIPTEN_EVENT_MOUSEDOWN:
-      {
-        boost::shared_ptr<OrthancStone::IFlexiblePointerTracker> t;
-
-        {
-          std::unique_ptr<OrthancStone::PointerEvent> event(
-            ConvertMouseEvent(*mouseEvent, controller->GetViewport()));
-
-          switch (mouseEvent->button)
-          {
-            case 0:  // Left button
-              emscripten_console_log("Creating RotateSceneTracker");
-              t.reset(new OrthancStone::RotateSceneTracker(
-                        controller, *event));
-              break;
-
-            case 1:  // Middle button
-              emscripten_console_log("Creating PanSceneTracker");
-              LOG(INFO) << "Creating PanSceneTracker" ;
-              t.reset(new OrthancStone::PanSceneTracker(
-                        controller, *event));
-              break;
-
-            case 2:  // Right button
-              emscripten_console_log("Creating ZoomSceneTracker");
-              t.reset(new OrthancStone::ZoomSceneTracker(
-                        controller, *event, controller->GetViewport().GetCanvasWidth()));
-              break;
-
-            default:
-              break;
-          }
-        }
-
-        if (t.get() != NULL)
-        {
-          tracker_.reset(
-            new OrthancStone::ActiveTracker(t, controller->GetViewport().GetCanvasIdentifier()));
-          controller->GetViewport().Refresh();
-        }
-
-        break;
-      }
-
-      case EMSCRIPTEN_EVENT_MOUSEMOVE:
-        if (tracker_.get() != NULL)
-        {
-          std::unique_ptr<OrthancStone::PointerEvent> event(
-            ConvertMouseEvent(*mouseEvent, controller->GetViewport()));
-          tracker_->PointerMove(*event);
-          controller->GetViewport().Refresh();
-        }
-        break;
-
-      case EMSCRIPTEN_EVENT_MOUSEUP:
-        if (tracker_.get() != NULL)
-        {
-          std::unique_ptr<OrthancStone::PointerEvent> event(
-            ConvertMouseEvent(*mouseEvent, controller->GetViewport()));
-          tracker_->PointerUp(*event);
-          controller->GetViewport().Refresh();
-          if (!tracker_->IsAlive())
-            tracker_.reset();
-        }
-        break;
-
-      default:
-        break;
-    }
-  }
-
-  return true;
-}
-
-
-void SetupEvents(const std::string& canvas,
-                 boost::shared_ptr<OrthancStone::ViewportController>& controller)
-{
-  emscripten_set_mousedown_callback(canvas.c_str(), &controller, false, OnMouseEvent);
-  emscripten_set_mousemove_callback(canvas.c_str(), &controller, false, OnMouseEvent);
-  emscripten_set_mouseup_callback(canvas.c_str(), &controller, false, OnMouseEvent);
-}
--- a/Samples/WebAssembly/index.html	Tue Apr 07 14:29:01 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-<!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>
-      <li><a href="BasicMPR.html">Basic MPR display</a></li>
-    </ul>
-  </body>
-</html>