changeset 754:92c400a09f1b

Merge from default
author Benjamin Golinvaux <bgo@osimis.io>
date Wed, 22 May 2019 16:13:46 +0200
parents a386bbc955dc (current diff) ab236bb5dbc7 (diff)
children 4a3b96630e6c
files Framework/Layers/CircleMeasureTracker.cpp Framework/Layers/CircleMeasureTracker.h Framework/Layers/ColorFrameRenderer.cpp Framework/Layers/ColorFrameRenderer.h Framework/Layers/DicomSeriesVolumeSlicer.cpp Framework/Layers/DicomSeriesVolumeSlicer.h Framework/Layers/DicomStructureSetSlicer.cpp Framework/Layers/DicomStructureSetSlicer.h Framework/Layers/FrameRenderer.cpp Framework/Layers/FrameRenderer.h Framework/Layers/GrayscaleFrameRenderer.cpp Framework/Layers/GrayscaleFrameRenderer.h Framework/Layers/ILayerRenderer.h Framework/Layers/IVolumeSlicer.h Framework/Layers/LineLayerRenderer.cpp Framework/Layers/LineLayerRenderer.h Framework/Layers/LineMeasureTracker.cpp Framework/Layers/LineMeasureTracker.h Framework/Layers/RenderStyle.cpp Framework/Layers/RenderStyle.h Framework/Layers/SeriesFrameRendererFactory.cpp Framework/Layers/SeriesFrameRendererFactory.h Framework/Layers/SingleFrameRendererFactory.cpp Framework/Layers/SingleFrameRendererFactory.h Framework/Layers/SliceOutlineRenderer.cpp Framework/Layers/SliceOutlineRenderer.h Framework/Scene2DViewport/AngleMeasureTool.h Framework/Scene2DViewport/LineMeasureTool.h Framework/Scene2DViewport/MeasureTools.h Framework/Scene2DViewport/MeasureToolsToolbox.cpp Framework/Scene2DViewport/MeasureToolsToolbox.h Framework/Scene2DViewport/ViewportController.cpp Framework/SmartLoader.cpp Framework/SmartLoader.h Framework/StoneException.h Framework/Toolbox/BaseWebService.cpp Framework/Toolbox/BaseWebService.h Framework/Toolbox/DicomFrameConverter.cpp Framework/Toolbox/DicomFrameConverter.h Framework/Toolbox/DownloadStack.cpp Framework/Toolbox/DownloadStack.h Framework/Toolbox/IDelayedCallExecutor.h Framework/Toolbox/IWebService.cpp Framework/Toolbox/IWebService.h Framework/Toolbox/OrientedBoundingBox.cpp Framework/Toolbox/OrientedBoundingBox.h Framework/Toolbox/OrthancApiClient.cpp Framework/Toolbox/OrthancApiClient.h Framework/Toolbox/OrthancSlicesLoader.cpp Framework/Toolbox/OrthancSlicesLoader.h Framework/Toolbox/Slice.cpp Framework/Toolbox/Slice.h Framework/Toolbox/ViewportGeometry.cpp Framework/Toolbox/ViewportGeometry.h Framework/Toolbox/VolumeImageGeometry.cpp Framework/Toolbox/VolumeImageGeometry.h Framework/Viewport/IMouseTracker.h Framework/Viewport/IStatusBar.h Framework/Viewport/IViewport.h Framework/Viewport/WidgetViewport.cpp Framework/Viewport/WidgetViewport.h Framework/Volumes/ISlicedVolume.h Framework/Volumes/IVolumeLoader.h Framework/Volumes/StructureSetLoader.cpp Framework/Volumes/StructureSetLoader.h Framework/Widgets/CairoWidget.cpp Framework/Widgets/CairoWidget.h Framework/Widgets/EmptyWidget.cpp Framework/Widgets/EmptyWidget.h Framework/Widgets/IWidget.h Framework/Widgets/IWorldSceneInteractor.h Framework/Widgets/IWorldSceneMouseTracker.h Framework/Widgets/LayoutWidget.cpp Framework/Widgets/LayoutWidget.h Framework/Widgets/PanMouseTracker.cpp Framework/Widgets/PanMouseTracker.h Framework/Widgets/PanZoomMouseTracker.cpp Framework/Widgets/PanZoomMouseTracker.h Framework/Widgets/SliceViewerWidget.cpp Framework/Widgets/SliceViewerWidget.h Framework/Widgets/TestCairoWidget.cpp Framework/Widgets/TestCairoWidget.h Framework/Widgets/TestWorldSceneWidget.cpp Framework/Widgets/TestWorldSceneWidget.h Framework/Widgets/WidgetBase.cpp Framework/Widgets/WidgetBase.h Framework/Widgets/WorldSceneWidget.cpp Framework/Widgets/WorldSceneWidget.h Framework/Widgets/ZoomMouseTracker.cpp Framework/Widgets/ZoomMouseTracker.h Framework/dev.h Resources/CMake/OrthancStoneConfiguration.cmake Resources/CodeGeneration/testWasmIntegrated/testWasmIntegratedCpp_api.yaml Resources/CodeGeneration/test_data/test1.jsonc Resources/CodeGeneration/test_data/test1.yaml Resources/CodeGeneration/test_data/test1_bogus_json.jsonc Resources/CodeGeneration/test_data/test1_bogus_schema.jsonc Samples/Sdl/TrackerSampleApp.cpp Samples/WebAssembly/BasicScene.cpp
diffstat 322 files changed, 16411 insertions(+), 15838 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/Generic/NativeStoneApplicationContext.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Applications/Generic/NativeStoneApplicationContext.cpp	Wed May 22 16:13:46 2019 +0200
@@ -24,7 +24,7 @@
 
 namespace OrthancStone
 {
-  IWidget& NativeStoneApplicationContext::GlobalMutexLocker::SetCentralWidget(IWidget* widget)
+  Deprecated::IWidget& NativeStoneApplicationContext::GlobalMutexLocker::SetCentralWidget(Deprecated::IWidget* widget)
   {
     that_.centralViewport_.SetCentralWidget(widget);
     return *widget;
--- a/Applications/Generic/NativeStoneApplicationContext.h	Wed May 22 16:01:34 2019 +0200
+++ b/Applications/Generic/NativeStoneApplicationContext.h	Wed May 22 16:13:46 2019 +0200
@@ -21,9 +21,9 @@
 
 #pragma once
 
-#include "../../Framework/Viewport/WidgetViewport.h"
-#include "../../Framework/Volumes/ISlicedVolume.h"
-#include "../../Framework/Volumes/IVolumeLoader.h"
+#include "../../Framework/Deprecated/Viewport/WidgetViewport.h"
+#include "../../Framework/Deprecated/Volumes/ISlicedVolume.h"
+#include "../../Framework/Deprecated/Volumes/IVolumeLoader.h"
 
 #include <list>
 #include <boost/thread.hpp>
@@ -37,7 +37,7 @@
     static void UpdateThread(NativeStoneApplicationContext* that);
 
     boost::recursive_mutex    globalMutex_;
-    WidgetViewport  centralViewport_;
+    Deprecated::WidgetViewport  centralViewport_;
     boost::thread   updateThread_;
     bool            stopped_;
     unsigned int    updateDelayInMs_;
@@ -56,9 +56,9 @@
       {
       }
 
-      IWidget& SetCentralWidget(IWidget* widget);   // Takes ownership
+      Deprecated::IWidget& SetCentralWidget(Deprecated::IWidget* widget);   // Takes ownership
 
-      IViewport& GetCentralViewport() 
+      Deprecated::IViewport& GetCentralViewport() 
       {
         return that_.centralViewport_;
       }
--- a/Applications/Generic/NativeStoneApplicationRunner.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Applications/Generic/NativeStoneApplicationRunner.cpp	Wed May 22 16:13:46 2019 +0200
@@ -19,8 +19,8 @@
  **/
 
 
-#if ORTHANC_ENABLE_NATIVE != 1
-#error this file shall be included only with the ORTHANC_ENABLE_NATIVE set to 1
+#if ORTHANC_ENABLE_THREADS != 1
+#error this file shall be included only with the ORTHANC_ENABLE_THREADS set to 1
 #endif
 
 #include "NativeStoneApplicationRunner.h"
@@ -43,7 +43,7 @@
   // Anonymous namespace to avoid clashes against other compilation modules
   namespace
   {
-    class LogStatusBar : public IStatusBar
+    class LogStatusBar : public Deprecated::IStatusBar
     {
     public:
       virtual void ClearMessage()
@@ -202,17 +202,17 @@
       {
         // use multiple threads to execute asynchronous tasks like 
         // download content
-        Oracle oracle(6); 
+        Deprecated::Oracle oracle(6); 
         oracle.Start();
 
         {
-          OracleWebService webService(
+          Deprecated::OracleWebService webService(
             broker_, oracle, webServiceParameters, context);
           
           context.SetWebService(webService);
           context.SetOrthancBaseUrl(webServiceParameters.GetUrl());
 
-          OracleDelayedCallExecutor delayedExecutor(broker_, oracle, context);
+          Deprecated::OracleDelayedCallExecutor delayedExecutor(broker_, oracle, context);
           context.SetDelayedCallExecutor(delayedExecutor);
 
           application_.Initialize(&context, statusBar, parameters);
--- a/Applications/Generic/NativeStoneApplicationRunner.h	Wed May 22 16:01:34 2019 +0200
+++ b/Applications/Generic/NativeStoneApplicationRunner.h	Wed May 22 16:13:46 2019 +0200
@@ -23,8 +23,8 @@
 
 #include "../IStoneApplication.h"
 
-#if ORTHANC_ENABLE_NATIVE != 1
-#error this file shall be included only with the ORTHANC_ENABLE_NATIVE set to 1
+#if ORTHANC_ENABLE_THREADS != 1
+#error this file shall be included only with the ORTHANC_ENABLE_THREADS set to 1
 #endif
 
 namespace OrthancStone
--- a/Applications/IStoneApplication.h	Wed May 22 16:01:34 2019 +0200
+++ b/Applications/IStoneApplication.h	Wed May 22 16:13:46 2019 +0200
@@ -22,9 +22,10 @@
 #pragma once
 
 #include "StoneApplicationContext.h"
+#include "../Framework/Deprecated/Viewport/WidgetViewport.h"
+
 #include <boost/program_options.hpp>
-#include "../Framework/Viewport/WidgetViewport.h"
-#include "json/json.h"
+#include <json/json.h>
 
 namespace OrthancStone
 {
@@ -47,7 +48,7 @@
 
     virtual void DeclareStartupOptions(boost::program_options::options_description& options) = 0;
     virtual void Initialize(StoneApplicationContext* context,
-                            IStatusBar& statusBar,
+                            Deprecated::IStatusBar& statusBar,
                             const boost::program_options::variables_map& parameters) = 0;
 
     /**
@@ -63,7 +64,7 @@
 #endif
 
     virtual std::string GetTitle() const = 0;
-    virtual IWidget* GetCentralWidget() = 0;
+    virtual Deprecated::IWidget* GetCentralWidget() = 0;
     virtual void Finalize() = 0;
   };
 }
--- a/Applications/Qt/QCairoWidget.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Applications/Qt/QCairoWidget.cpp	Wed May 22 16:13:46 2019 +0200
@@ -27,14 +27,14 @@
 
 
 QCairoWidget::StoneObserver::StoneObserver(QCairoWidget& that,
-                                           OrthancStone::IViewport& viewport,
+                                           Deprecated::IViewport& viewport,
                                            OrthancStone::MessageBroker& broker) :
   OrthancStone::IObserver(broker),
   that_(that)
 {
   // get notified each time the content of the central viewport changes
   viewport.RegisterObserverCallback(
-    new OrthancStone::Callable<StoneObserver, OrthancStone::IViewport::ViewportChangedMessage>
+    new OrthancStone::Callable<StoneObserver, Deprecated::IViewport::ViewportChangedMessage>
     (*this, &StoneObserver::OnViewportChanged));
 }
 
@@ -68,7 +68,7 @@
       context_ != NULL)
   {
     OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_);
-    OrthancStone::IViewport& viewport = locker.GetCentralViewport();
+    Deprecated::IViewport& viewport = locker.GetCentralViewport();
     Orthanc::ImageAccessor a;
     surface_.GetWriteableAccessor(a);
     viewport.Render(a);
@@ -125,7 +125,7 @@
 
   {
     OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_);
-    locker.GetCentralViewport().MouseDown(button, event->pos().x(), event->pos().y(), stoneModifiers, std::vector<OrthancStone::Touch>());
+    locker.GetCentralViewport().MouseDown(button, event->pos().x(), event->pos().y(), stoneModifiers, std::vector<Deprecated::Touch>());
   }
 }
 
@@ -140,7 +140,7 @@
 void QCairoWidget::mouseMoveEvent(QMouseEvent* event)
 {
   OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_);
-  locker.GetCentralViewport().MouseMove(event->pos().x(), event->pos().y(), std::vector<OrthancStone::Touch>());
+  locker.GetCentralViewport().MouseMove(event->pos().x(), event->pos().y(), std::vector<Deprecated::Touch>());
 }
 
 
--- a/Applications/Qt/QCairoWidget.h	Wed May 22 16:01:34 2019 +0200
+++ b/Applications/Qt/QCairoWidget.h	Wed May 22 16:13:46 2019 +0200
@@ -40,10 +40,10 @@
     
   public:
     StoneObserver(QCairoWidget& that,
-                  OrthancStone::IViewport& viewport,
+                  Deprecated::IViewport& viewport,
                   OrthancStone::MessageBroker& broker);
 
-    void OnViewportChanged(const OrthancStone::IViewport::ViewportChangedMessage& message)
+    void OnViewportChanged(const Deprecated::IViewport::ViewportChangedMessage& message)
     {
       that_.OnViewportChanged();
     }
--- a/Applications/Samples/CMakeLists.txt	Wed May 22 16:01:34 2019 +0200
+++ b/Applications/Samples/CMakeLists.txt	Wed May 22 16:13:46 2019 +0200
@@ -8,6 +8,9 @@
 
 include(../../Resources/CMake/OrthancStoneParameters.cmake)
 
+set(ENABLE_STONE_DEPRECATED ON)  # Need deprecated classes for these samples
+
+
 if (OPENSSL_NO_CAPIENG)
 add_definitions(-DOPENSSL_NO_CAPIENG=1)
 endif()
@@ -208,7 +211,6 @@
       ${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/Messages.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
--- a/Applications/Samples/SampleApplicationBase.h	Wed May 22 16:01:34 2019 +0200
+++ b/Applications/Samples/SampleApplicationBase.h	Wed May 22 16:13:46 2019 +0200
@@ -22,7 +22,7 @@
 #pragma once
 
 #include "../../Applications/IStoneApplication.h"
-#include "../../Framework/Widgets/WorldSceneWidget.h"
+#include "../../Framework/Deprecated/Widgets/WorldSceneWidget.h"
 
 #if ORTHANC_ENABLE_WASM==1
 #include "../../Platforms/Wasm/WasmPlatformApplicationAdapter.h"
@@ -42,11 +42,11 @@
     {
     protected:
       // ownership is transferred to the application context
-      WorldSceneWidget*  mainWidget_;
+      Deprecated::WorldSceneWidget*  mainWidget_;
 
     public:
       virtual void Initialize(StoneApplicationContext* context,
-                              IStatusBar& statusBar,
+                              Deprecated::IStatusBar& statusBar,
                               const boost::program_options::variables_map& parameters) ORTHANC_OVERRIDE
       {
       }
@@ -64,7 +64,7 @@
 
 
       virtual void Finalize() ORTHANC_OVERRIDE {}
-      virtual IWidget* GetCentralWidget() ORTHANC_OVERRIDE {return mainWidget_;}
+      virtual Deprecated::IWidget* GetCentralWidget() ORTHANC_OVERRIDE {return mainWidget_;}
 
 #if ORTHANC_ENABLE_WASM==1
       // default implementations for a single canvas named "canvas" in the HTML and an emtpy WasmApplicationAdapter
--- a/Applications/Samples/SimpleViewer/MainWidgetInteractor.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Applications/Samples/SimpleViewer/MainWidgetInteractor.cpp	Wed May 22 16:13:46 2019 +0200
@@ -24,27 +24,27 @@
 
 namespace SimpleViewer {
 
-  IWorldSceneMouseTracker* MainWidgetInteractor::CreateMouseTracker(WorldSceneWidget& widget,
-                                                                    const ViewportGeometry& view,
+  Deprecated::IWorldSceneMouseTracker* MainWidgetInteractor::CreateMouseTracker(Deprecated::WorldSceneWidget& widget,
+                                                                    const Deprecated::ViewportGeometry& view,
                                                                     MouseButton button,
                                                                     KeyboardModifiers modifiers,
                                                                     int viewportX,
                                                                     int viewportY,
                                                                     double x,
                                                                     double y,
-                                                                    IStatusBar* statusBar,
-                                                                    const std::vector<Touch>& displayTouches)
+                                                                    Deprecated::IStatusBar* statusBar,
+                                                                    const std::vector<Deprecated::Touch>& displayTouches)
   {
     if (button == MouseButton_Left)
     {
       if (application_.GetCurrentTool() == Tool_LineMeasure)
       {
-        return new LineMeasureTracker(statusBar, dynamic_cast<SliceViewerWidget&>(widget).GetSlice(),
+        return new Deprecated::LineMeasureTracker(statusBar, dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice(),
                                       x, y, 255, 0, 0, application_.GetFont());
       }
       else if (application_.GetCurrentTool() == Tool_CircleMeasure)
       {
-        return new CircleMeasureTracker(statusBar, dynamic_cast<SliceViewerWidget&>(widget).GetSlice(),
+        return new Deprecated::CircleMeasureTracker(statusBar, dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice(),
                                         x, y, 255, 0, 0, application_.GetFont());
       }
       else if (application_.GetCurrentTool() == Tool_Crop)
@@ -68,15 +68,15 @@
   }
 
   void MainWidgetInteractor::MouseOver(CairoContext& context,
-                                       WorldSceneWidget& widget,
-                                       const ViewportGeometry& view,
+                                       Deprecated::WorldSceneWidget& widget,
+                                       const Deprecated::ViewportGeometry& view,
                                        double x,
                                        double y,
-                                       IStatusBar* statusBar)
+                                       Deprecated::IStatusBar* statusBar)
   {
     if (statusBar != NULL)
     {
-      Vector p = dynamic_cast<SliceViewerWidget&>(widget).GetSlice().MapSliceToWorldCoordinates(x, y);
+      Vector p = dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice().MapSliceToWorldCoordinates(x, y);
 
       char buf[64];
       sprintf(buf, "X = %.02f Y = %.02f Z = %.02f (in cm)",
@@ -85,18 +85,18 @@
     }
   }
 
-  void MainWidgetInteractor::MouseWheel(WorldSceneWidget& widget,
+  void MainWidgetInteractor::MouseWheel(Deprecated::WorldSceneWidget& widget,
                                         MouseWheelDirection direction,
                                         KeyboardModifiers modifiers,
-                                        IStatusBar* statusBar)
+                                        Deprecated::IStatusBar* statusBar)
   {
   }
 
-  void MainWidgetInteractor::KeyPressed(WorldSceneWidget& widget,
+  void MainWidgetInteractor::KeyPressed(Deprecated::WorldSceneWidget& widget,
                                         KeyboardKeys key,
                                         char keyChar,
                                         KeyboardModifiers modifiers,
-                                        IStatusBar* statusBar)
+                                        Deprecated::IStatusBar* statusBar)
   {
     switch (keyChar)
     {
--- a/Applications/Samples/SimpleViewer/MainWidgetInteractor.h	Wed May 22 16:01:34 2019 +0200
+++ b/Applications/Samples/SimpleViewer/MainWidgetInteractor.h	Wed May 22 16:13:46 2019 +0200
@@ -20,7 +20,7 @@
 
 #pragma once
 
-#include "Framework/Widgets/IWorldSceneInteractor.h"
+#include "../../../Framework/Deprecated/Widgets/IWorldSceneInteractor.h"
 
 using namespace OrthancStone;
 
@@ -28,7 +28,7 @@
 
   class SimpleViewerApplication;
 
-  class MainWidgetInteractor : public IWorldSceneInteractor
+  class MainWidgetInteractor : public Deprecated::IWorldSceneInteractor
   {
   private:
     SimpleViewerApplication&  application_;
@@ -42,34 +42,34 @@
     /**
         WorldSceneWidget: 
     */
-    virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget,
-                                                        const ViewportGeometry& view,
-                                                        MouseButton button,
-                                                        KeyboardModifiers modifiers,
-                                                        int viewportX,
-                                                        int viewportY,
-                                                        double x,
-                                                        double y,
-                                                        IStatusBar* statusBar,
-                                                        const std::vector<Touch>& displayTouches);
+    virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& widget,
+                                                                    const Deprecated::ViewportGeometry& view,
+                                                                    MouseButton button,
+                                                                    KeyboardModifiers modifiers,
+                                                                    int viewportX,
+                                                                    int viewportY,
+                                                                    double x,
+                                                                    double y,
+                                                                    Deprecated::IStatusBar* statusBar,
+                                                                    const std::vector<Deprecated::Touch>& displayTouches);
 
     virtual void MouseOver(CairoContext& context,
-                           WorldSceneWidget& widget,
-                           const ViewportGeometry& view,
+                           Deprecated::WorldSceneWidget& widget,
+                           const Deprecated::ViewportGeometry& view,
                            double x,
                            double y,
-                           IStatusBar* statusBar);
+                           Deprecated::IStatusBar* statusBar);
 
-    virtual void MouseWheel(WorldSceneWidget& widget,
+    virtual void MouseWheel(Deprecated::WorldSceneWidget& widget,
                             MouseWheelDirection direction,
                             KeyboardModifiers modifiers,
-                            IStatusBar* statusBar);
+                            Deprecated::IStatusBar* statusBar);
 
-    virtual void KeyPressed(WorldSceneWidget& widget,
+    virtual void KeyPressed(Deprecated::WorldSceneWidget& widget,
                             KeyboardKeys key,
                             char keyChar,
                             KeyboardModifiers modifiers,
-                            IStatusBar* statusBar);
+                            Deprecated::IStatusBar* statusBar);
   };
 
 
--- a/Applications/Samples/SimpleViewer/SimpleViewerApplication.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Applications/Samples/SimpleViewer/SimpleViewerApplication.cpp	Wed May 22 16:13:46 2019 +0200
@@ -33,28 +33,26 @@
 {
 
   void SimpleViewerApplication::Initialize(StoneApplicationContext* context,
-                                           IStatusBar& statusBar,
+                                           Deprecated::IStatusBar& statusBar,
                                            const boost::program_options::variables_map& parameters)
   {
-    using namespace OrthancStone;
-
     context_ = context;
     statusBar_ = &statusBar;
 
     {// initialize viewports and layout
-      mainLayout_ = new LayoutWidget("main-layout");
+      mainLayout_ = new Deprecated::LayoutWidget("main-layout");
       mainLayout_->SetPadding(10);
       mainLayout_->SetBackgroundCleared(true);
       mainLayout_->SetBackgroundColor(0, 0, 0);
       mainLayout_->SetHorizontal();
 
-      thumbnailsLayout_ = new LayoutWidget("thumbnail-layout");
+      thumbnailsLayout_ = new Deprecated::LayoutWidget("thumbnail-layout");
       thumbnailsLayout_->SetPadding(10);
       thumbnailsLayout_->SetBackgroundCleared(true);
       thumbnailsLayout_->SetBackgroundColor(50, 50, 50);
       thumbnailsLayout_->SetVertical();
 
-      mainWidget_ = new SliceViewerWidget(IObserver::GetBroker(), "main-viewport");
+      mainWidget_ = new Deprecated::SliceViewerWidget(IObserver::GetBroker(), "main-viewport");
       //mainWidget_->RegisterObserver(*this);
 
       // hierarchy
@@ -62,8 +60,8 @@
       mainLayout_->AddWidget(mainWidget_);
 
       // sources
-      smartLoader_.reset(new SmartLoader(IObserver::GetBroker(), context->GetOrthancApiClient()));
-      smartLoader_->SetImageQuality(SliceImageQuality_FullPam);
+      smartLoader_.reset(new Deprecated::SmartLoader(IObserver::GetBroker(), context->GetOrthancApiClient()));
+      smartLoader_->SetImageQuality(Deprecated::SliceImageQuality_FullPam);
 
       mainLayout_->SetTransmitMouseOver(true);
       mainWidgetInteractor_.reset(new MainWidgetInteractor(*this));
@@ -78,7 +76,7 @@
     if (parameters.count("studyId") < 1)
     {
       LOG(WARNING) << "The study ID is missing, will take the first studyId found in Orthanc";
-      context->GetOrthancApiClient().GetJsonAsync("/studies", new Callable<SimpleViewerApplication, OrthancApiClient::JsonResponseReadyMessage>(*this, &SimpleViewerApplication::OnStudyListReceived));
+      context->GetOrthancApiClient().GetJsonAsync("/studies", new Callable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage>(*this, &SimpleViewerApplication::OnStudyListReceived));
     }
     else
     {
@@ -98,7 +96,7 @@
     options.add(generic);
   }
 
-  void SimpleViewerApplication::OnStudyListReceived(const OrthancApiClient::JsonResponseReadyMessage& message)
+  void SimpleViewerApplication::OnStudyListReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message)
   {
     const Json::Value& response = message.GetJson();
 
@@ -108,7 +106,7 @@
       SelectStudy(response[0].asString());
     }
   }
-  void SimpleViewerApplication::OnStudyReceived(const OrthancApiClient::JsonResponseReadyMessage& message)
+  void SimpleViewerApplication::OnStudyReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message)
   {
     const Json::Value& response = message.GetJson();
 
@@ -116,12 +114,12 @@
     {
       for (size_t i=0; i < response["Series"].size(); i++)
       {
-        context_->GetOrthancApiClient().GetJsonAsync("/series/" + response["Series"][(int)i].asString(), new Callable<SimpleViewerApplication, OrthancApiClient::JsonResponseReadyMessage>(*this, &SimpleViewerApplication::OnSeriesReceived));
+        context_->GetOrthancApiClient().GetJsonAsync("/series/" + response["Series"][(int)i].asString(), new Callable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage>(*this, &SimpleViewerApplication::OnSeriesReceived));
       }
     }
   }
 
-  void SimpleViewerApplication::OnSeriesReceived(const OrthancApiClient::JsonResponseReadyMessage& message)
+  void SimpleViewerApplication::OnSeriesReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message)
   {
     const Json::Value& response = message.GetJson();
 
@@ -154,13 +152,13 @@
   {
     LOG(INFO) << "Loading thumbnail for series " << seriesId;
     
-    SliceViewerWidget* thumbnailWidget = 
-      new SliceViewerWidget(IObserver::GetBroker(), "thumbnail-series-" + seriesId);
+    Deprecated::SliceViewerWidget* thumbnailWidget = 
+      new Deprecated::SliceViewerWidget(IObserver::GetBroker(), "thumbnail-series-" + seriesId);
     thumbnails_.push_back(thumbnailWidget);
     thumbnailsLayout_->AddWidget(thumbnailWidget);
     
     thumbnailWidget->RegisterObserverCallback(
-      new Callable<SimpleViewerApplication, SliceViewerWidget::GeometryChangedMessage>
+      new Callable<SimpleViewerApplication, Deprecated::SliceViewerWidget::GeometryChangedMessage>
       (*this, &SimpleViewerApplication::OnWidgetGeometryChanged));
     
     smartLoader_->SetFrameInWidget(*thumbnailWidget, 0, instanceId, 0);
@@ -169,13 +167,13 @@
 
   void SimpleViewerApplication::SelectStudy(const std::string& studyId)
   {
-    context_->GetOrthancApiClient().GetJsonAsync("/studies/" + studyId, new Callable<SimpleViewerApplication, OrthancApiClient::JsonResponseReadyMessage>(*this, &SimpleViewerApplication::OnStudyReceived));
+    context_->GetOrthancApiClient().GetJsonAsync("/studies/" + studyId, new Callable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage>(*this, &SimpleViewerApplication::OnStudyReceived));
   }
 
-  void SimpleViewerApplication::OnWidgetGeometryChanged(const SliceViewerWidget::GeometryChangedMessage& message)
+  void SimpleViewerApplication::OnWidgetGeometryChanged(const Deprecated::SliceViewerWidget::GeometryChangedMessage& message)
   {
     // TODO: The "const_cast" could probably be replaced by "mainWidget_"
-    const_cast<SliceViewerWidget&>(message.GetOrigin()).FitContent();
+    const_cast<Deprecated::SliceViewerWidget&>(message.GetOrigin()).FitContent();
   }
 
   void SimpleViewerApplication::SelectSeriesInMainViewport(const std::string& seriesId)
--- a/Applications/Samples/SimpleViewer/SimpleViewerApplication.h	Wed May 22 16:01:34 2019 +0200
+++ b/Applications/Samples/SimpleViewer/SimpleViewerApplication.h	Wed May 22 16:13:46 2019 +0200
@@ -29,12 +29,12 @@
 
 #include "Applications/IStoneApplication.h"
 
-#include "Framework/Layers/CircleMeasureTracker.h"
-#include "Framework/Layers/LineMeasureTracker.h"
-#include "Framework/Widgets/SliceViewerWidget.h"
-#include "Framework/Widgets/LayoutWidget.h"
-#include "Framework/Messages/IObserver.h"
-#include "Framework/SmartLoader.h"
+#include "../../../Framework/Deprecated/Layers/CircleMeasureTracker.h"
+#include "../../../Framework/Deprecated/Layers/LineMeasureTracker.h"
+#include "../../../Framework/Deprecated/SmartLoader.h"
+#include "../../../Framework/Deprecated/Widgets/LayoutWidget.h"
+#include "../../../Framework/Deprecated/Widgets/SliceViewerWidget.h"
+#include "../../../Framework/Messages/IObserver.h"
 
 #if ORTHANC_ENABLE_WASM==1
 #include "Platforms/Wasm/WasmPlatformApplicationAdapter.h"
@@ -83,18 +83,18 @@
 
     std::auto_ptr<MainWidgetInteractor> mainWidgetInteractor_;
     std::auto_ptr<ThumbnailInteractor>  thumbnailInteractor_;
-    LayoutWidget*                       mainLayout_;
-    LayoutWidget*                       thumbnailsLayout_;
-    SliceViewerWidget*                  mainWidget_;
-    std::vector<SliceViewerWidget*>     thumbnails_;
+    Deprecated::LayoutWidget*                       mainLayout_;
+    Deprecated::LayoutWidget*                       thumbnailsLayout_;
+    Deprecated::SliceViewerWidget*                  mainWidget_;
+    std::vector<Deprecated::SliceViewerWidget*>     thumbnails_;
     std::map<std::string, std::vector<std::string> > instancesIdsPerSeriesId_;
     std::map<std::string, Json::Value>  seriesTags_;
     unsigned int                        currentInstanceIndex_;
-    OrthancStone::WidgetViewport*       wasmViewport1_;
-    OrthancStone::WidgetViewport*       wasmViewport2_;
+    Deprecated::WidgetViewport*       wasmViewport1_;
+    Deprecated::WidgetViewport*       wasmViewport2_;
 
-    IStatusBar*                         statusBar_;
-    std::auto_ptr<SmartLoader>          smartLoader_;
+    Deprecated::IStatusBar*                         statusBar_;
+    std::auto_ptr<Deprecated::SmartLoader>          smartLoader_;
 
     Orthanc::Font                       font_;
 
@@ -112,24 +112,24 @@
     }
 
     virtual void Finalize() ORTHANC_OVERRIDE {}
-    virtual IWidget* GetCentralWidget() ORTHANC_OVERRIDE {return mainLayout_;}
+    virtual Deprecated::IWidget* GetCentralWidget() ORTHANC_OVERRIDE {return mainLayout_;}
 
     virtual void DeclareStartupOptions(boost::program_options::options_description& options) ORTHANC_OVERRIDE;
     virtual void Initialize(StoneApplicationContext* context,
-                            IStatusBar& statusBar,
+                            Deprecated::IStatusBar& statusBar,
                             const boost::program_options::variables_map& parameters) ORTHANC_OVERRIDE;
 
-    void OnStudyListReceived(const OrthancApiClient::JsonResponseReadyMessage& message);
+    void OnStudyListReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message);
 
-    void OnStudyReceived(const OrthancApiClient::JsonResponseReadyMessage& message);
+    void OnStudyReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message);
 
-    void OnSeriesReceived(const OrthancApiClient::JsonResponseReadyMessage& message);
+    void OnSeriesReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message);
 
     void LoadThumbnailForSeries(const std::string& seriesId, const std::string& instanceId);
 
     void SelectStudy(const std::string& studyId);
 
-    void OnWidgetGeometryChanged(const SliceViewerWidget::GeometryChangedMessage& message);
+    void OnWidgetGeometryChanged(const Deprecated::SliceViewerWidget::GeometryChangedMessage& message);
 
     void SelectSeriesInMainViewport(const std::string& seriesId);
 
--- a/Applications/Samples/SimpleViewer/ThumbnailInteractor.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Applications/Samples/SimpleViewer/ThumbnailInteractor.cpp	Wed May 22 16:13:46 2019 +0200
@@ -24,16 +24,16 @@
 
 namespace SimpleViewer {
 
-  IWorldSceneMouseTracker* ThumbnailInteractor::CreateMouseTracker(WorldSceneWidget& widget,
-                                                                   const ViewportGeometry& view,
+  Deprecated::IWorldSceneMouseTracker* ThumbnailInteractor::CreateMouseTracker(Deprecated::WorldSceneWidget& widget,
+                                                                   const Deprecated::ViewportGeometry& view,
                                                                    MouseButton button,
                                                                    KeyboardModifiers modifiers,
                                                                    int viewportX,
                                                                    int viewportY,
                                                                    double x,
                                                                    double y,
-                                                                   IStatusBar* statusBar,
-                                                                   const std::vector<Touch>& displayTouches)
+                                                                   Deprecated::IStatusBar* statusBar,
+                                                                   const std::vector<Deprecated::Touch>& displayTouches)
   {
     if (button == MouseButton_Left)
     {
--- a/Applications/Samples/SimpleViewer/ThumbnailInteractor.h	Wed May 22 16:01:34 2019 +0200
+++ b/Applications/Samples/SimpleViewer/ThumbnailInteractor.h	Wed May 22 16:13:46 2019 +0200
@@ -21,7 +21,7 @@
 
 #pragma once
 
-#include "Framework/Widgets/IWorldSceneInteractor.h"
+#include "../../../Framework/Deprecated/Widgets/IWorldSceneInteractor.h"
 
 using namespace OrthancStone;
 
@@ -29,7 +29,7 @@
 
   class SimpleViewerApplication;
 
-  class ThumbnailInteractor : public IWorldSceneInteractor
+  class ThumbnailInteractor : public Deprecated::IWorldSceneInteractor
   {
   private:
     SimpleViewerApplication&  application_;
@@ -39,36 +39,36 @@
     {
     }
 
-    virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget,
-                                                        const ViewportGeometry& view,
-                                                        MouseButton button,
-                                                        KeyboardModifiers modifiers,
-                                                        int viewportX,
-                                                        int viewportY,
-                                                        double x,
-                                                        double y,
-                                                        IStatusBar* statusBar,
-                                                        const std::vector<Touch>& displayTouches);
+    virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& widget,
+                                                                    const Deprecated::ViewportGeometry& view,
+                                                                    MouseButton button,
+                                                                    KeyboardModifiers modifiers,
+                                                                    int viewportX,
+                                                                    int viewportY,
+                                                                    double x,
+                                                                    double y,
+                                                                    Deprecated::IStatusBar* statusBar,
+                                                                    const std::vector<Deprecated::Touch>& displayTouches);
 
     virtual void MouseOver(CairoContext& context,
-                           WorldSceneWidget& widget,
-                           const ViewportGeometry& view,
+                           Deprecated::WorldSceneWidget& widget,
+                           const Deprecated::ViewportGeometry& view,
                            double x,
                            double y,
-                           IStatusBar* statusBar)
+                           Deprecated::IStatusBar* statusBar)
     {}
 
-    virtual void MouseWheel(WorldSceneWidget& widget,
+    virtual void MouseWheel(Deprecated::WorldSceneWidget& widget,
                             MouseWheelDirection direction,
                             KeyboardModifiers modifiers,
-                            IStatusBar* statusBar)
+                            Deprecated::IStatusBar* statusBar)
     {}
 
-    virtual void KeyPressed(WorldSceneWidget& widget,
+    virtual void KeyPressed(Deprecated::WorldSceneWidget& widget,
                             KeyboardKeys key,
                             char keyChar,
                             KeyboardModifiers modifiers,
-                            IStatusBar* statusBar)
+                            Deprecated::IStatusBar* statusBar)
     {}
 
   };
--- a/Applications/Samples/SimpleViewerApplicationSingleFile.h	Wed May 22 16:01:34 2019 +0200
+++ b/Applications/Samples/SimpleViewerApplicationSingleFile.h	Wed May 22 16:13:46 2019 +0200
@@ -23,12 +23,12 @@
 
 #include "SampleApplicationBase.h"
 
-#include "../../Framework/Layers/CircleMeasureTracker.h"
-#include "../../Framework/Layers/LineMeasureTracker.h"
-#include "../../Framework/Widgets/SliceViewerWidget.h"
-#include "../../Framework/Widgets/LayoutWidget.h"
+#include "../../Framework/Deprecated/Layers/CircleMeasureTracker.h"
+#include "../../Framework/Deprecated/Layers/LineMeasureTracker.h"
+#include "../../Framework/Deprecated/SmartLoader.h"
+#include "../../Framework/Deprecated/Widgets/LayoutWidget.h"
+#include "../../Framework/Deprecated/Widgets/SliceViewerWidget.h"
 #include "../../Framework/Messages/IObserver.h"
-#include "../../Framework/SmartLoader.h"
 
 #if ORTHANC_ENABLE_WASM==1
 #include "../../Platforms/Wasm/WasmPlatformApplicationAdapter.h"
@@ -47,7 +47,7 @@
       public IObserver
     {
     private:
-      class ThumbnailInteractor : public IWorldSceneInteractor
+      class ThumbnailInteractor : public Deprecated::IWorldSceneInteractor
       {
       private:
         SimpleViewerApplication&  application_;
@@ -58,16 +58,16 @@
         {
         }
 
-        virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget,
-                                                            const ViewportGeometry& view,
+        virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& widget,
+                                                            const Deprecated::ViewportGeometry& view,
                                                             MouseButton button,
                                                             KeyboardModifiers modifiers,
                                                             int viewportX,
                                                             int viewportY,
                                                             double x,
                                                             double y,
-                                                            IStatusBar* statusBar,
-                                                            const std::vector<Touch>& displayTouches)
+                                                            Deprecated::IStatusBar* statusBar,
+                                                            const std::vector<Deprecated::Touch>& displayTouches)
         {
           if (button == MouseButton_Left)
           {
@@ -79,31 +79,31 @@
         }
 
         virtual void MouseOver(CairoContext& context,
-                               WorldSceneWidget& widget,
-                               const ViewportGeometry& view,
+                               Deprecated::WorldSceneWidget& widget,
+                               const Deprecated::ViewportGeometry& view,
                                double x,
                                double y,
-                               IStatusBar* statusBar)
+                               Deprecated::IStatusBar* statusBar)
         {
         }
 
-        virtual void MouseWheel(WorldSceneWidget& widget,
+        virtual void MouseWheel(Deprecated::WorldSceneWidget& widget,
                                 MouseWheelDirection direction,
                                 KeyboardModifiers modifiers,
-                                IStatusBar* statusBar)
+                                Deprecated::IStatusBar* statusBar)
         {
         }
 
-        virtual void KeyPressed(WorldSceneWidget& widget,
+        virtual void KeyPressed(Deprecated::WorldSceneWidget& widget,
                                 KeyboardKeys key,
                                 char keyChar,
                                 KeyboardModifiers modifiers,
-                                IStatusBar* statusBar)
+                                Deprecated::IStatusBar* statusBar)
         {
         }
       };
 
-      class MainWidgetInteractor : public IWorldSceneInteractor
+      class MainWidgetInteractor : public Deprecated::IWorldSceneInteractor
       {
       private:
         SimpleViewerApplication&  application_;
@@ -114,27 +114,27 @@
         {
         }
         
-        virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget,
-                                                            const ViewportGeometry& view,
+        virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& widget,
+                                                            const Deprecated::ViewportGeometry& view,
                                                             MouseButton button,
                                                             KeyboardModifiers modifiers,
                                                             int viewportX,
                                                             int viewportY,
                                                             double x,
                                                             double y,
-                                                            IStatusBar* statusBar,
-                                                            const std::vector<Touch>& displayTouches)
+                                                            Deprecated::IStatusBar* statusBar,
+                                                            const std::vector<Deprecated::Touch>& displayTouches)
         {
           if (button == MouseButton_Left)
           {
             if (application_.currentTool_ == Tool_LineMeasure)
             {
-              return new LineMeasureTracker(statusBar, dynamic_cast<SliceViewerWidget&>(widget).GetSlice(),
+              return new Deprecated::LineMeasureTracker(statusBar, dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice(),
                                             x, y, 255, 0, 0, application_.GetFont());
             }
             else if (application_.currentTool_ == Tool_CircleMeasure)
             {
-              return new CircleMeasureTracker(statusBar, dynamic_cast<SliceViewerWidget&>(widget).GetSlice(),
+              return new Deprecated::CircleMeasureTracker(statusBar, dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice(),
                                               x, y, 255, 0, 0, application_.GetFont());
             }
           }
@@ -142,15 +142,15 @@
         }
 
         virtual void MouseOver(CairoContext& context,
-                               WorldSceneWidget& widget,
-                               const ViewportGeometry& view,
+                               Deprecated::WorldSceneWidget& widget,
+                               const Deprecated::ViewportGeometry& view,
                                double x,
                                double y,
-                               IStatusBar* statusBar)
+                               Deprecated::IStatusBar* statusBar)
         {
           if (statusBar != NULL)
           {
-            Vector p = dynamic_cast<SliceViewerWidget&>(widget).GetSlice().MapSliceToWorldCoordinates(x, y);
+            Vector p = dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice().MapSliceToWorldCoordinates(x, y);
             
             char buf[64];
             sprintf(buf, "X = %.02f Y = %.02f Z = %.02f (in cm)",
@@ -159,18 +159,18 @@
           }
         }
 
-        virtual void MouseWheel(WorldSceneWidget& widget,
+        virtual void MouseWheel(Deprecated::WorldSceneWidget& widget,
                                 MouseWheelDirection direction,
                                 KeyboardModifiers modifiers,
-                                IStatusBar* statusBar)
+                                Deprecated::IStatusBar* statusBar)
         {
         }
 
-        virtual void KeyPressed(WorldSceneWidget& widget,
+        virtual void KeyPressed(Deprecated::WorldSceneWidget& widget,
                                 KeyboardKeys key,
                                 char keyChar,
                                 KeyboardModifiers modifiers,
-                                IStatusBar* statusBar)
+                                Deprecated::IStatusBar* statusBar)
         {
           switch (keyChar)
           {
@@ -241,19 +241,19 @@
       Tool                                 currentTool_;
       std::auto_ptr<MainWidgetInteractor>  mainWidgetInteractor_;
       std::auto_ptr<ThumbnailInteractor>   thumbnailInteractor_;
-      LayoutWidget*                        mainLayout_;
-      LayoutWidget*                        thumbnailsLayout_;
-      std::vector<SliceViewerWidget*>      thumbnails_;
+      Deprecated::LayoutWidget*                        mainLayout_;
+      Deprecated::LayoutWidget*                        thumbnailsLayout_;
+      std::vector<Deprecated::SliceViewerWidget*>      thumbnails_;
 
       std::map<std::string, std::vector<std::string> > instancesIdsPerSeriesId_;
       std::map<std::string, Json::Value> seriesTags_;
 
       unsigned int                         currentInstanceIndex_;
-      OrthancStone::WidgetViewport*        wasmViewport1_;
-      OrthancStone::WidgetViewport*        wasmViewport2_;
+      Deprecated::WidgetViewport*        wasmViewport1_;
+      Deprecated::WidgetViewport*        wasmViewport2_;
 
-      IStatusBar*                          statusBar_;
-      std::auto_ptr<SmartLoader>           smartLoader_;
+      Deprecated::IStatusBar*                          statusBar_;
+      std::auto_ptr<Deprecated::SmartLoader>           smartLoader_;
 
       Orthanc::Font                        font_;
 
@@ -282,7 +282,7 @@
       }
 
       virtual void Initialize(StoneApplicationContext* context,
-                              IStatusBar& statusBar,
+                              Deprecated::IStatusBar& statusBar,
                               const boost::program_options::variables_map& parameters)
       {
         using namespace OrthancStone;
@@ -291,19 +291,19 @@
         statusBar_ = &statusBar;
 
         {// initialize viewports and layout
-          mainLayout_ = new LayoutWidget("main-layout");
+          mainLayout_ = new Deprecated::LayoutWidget("main-layout");
           mainLayout_->SetPadding(10);
           mainLayout_->SetBackgroundCleared(true);
           mainLayout_->SetBackgroundColor(0, 0, 0);
           mainLayout_->SetHorizontal();
 
-          thumbnailsLayout_ = new LayoutWidget("thumbnail-layout");
+          thumbnailsLayout_ = new Deprecated::LayoutWidget("thumbnail-layout");
           thumbnailsLayout_->SetPadding(10);
           thumbnailsLayout_->SetBackgroundCleared(true);
           thumbnailsLayout_->SetBackgroundColor(50, 50, 50);
           thumbnailsLayout_->SetVertical();
 
-          mainWidget_ = new SliceViewerWidget(GetBroker(), "main-viewport");
+          mainWidget_ = new Deprecated::SliceViewerWidget(GetBroker(), "main-viewport");
           //mainWidget_->RegisterObserver(*this);
 
           // hierarchy
@@ -311,8 +311,8 @@
           mainLayout_->AddWidget(mainWidget_);
 
           // sources
-          smartLoader_.reset(new SmartLoader(GetBroker(), context->GetOrthancApiClient()));
-          smartLoader_->SetImageQuality(SliceImageQuality_FullPam);
+          smartLoader_.reset(new Deprecated::SmartLoader(GetBroker(), context->GetOrthancApiClient()));
+          smartLoader_->SetImageQuality(Deprecated::SliceImageQuality_FullPam);
 
           mainLayout_->SetTransmitMouseOver(true);
           mainWidgetInteractor_.reset(new MainWidgetInteractor(*this));
@@ -329,7 +329,7 @@
           LOG(WARNING) << "The study ID is missing, will take the first studyId found in Orthanc";
           context->GetOrthancApiClient().GetJsonAsync(
             "/studies",
-            new Callable<SimpleViewerApplication, OrthancApiClient::JsonResponseReadyMessage>
+            new Callable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage>
             (*this, &SimpleViewerApplication::OnStudyListReceived));
         }
         else
@@ -338,7 +338,7 @@
         }
       }
 
-      void OnStudyListReceived(const OrthancApiClient::JsonResponseReadyMessage& message)
+      void OnStudyListReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message)
       {
         const Json::Value& response = message.GetJson();
 
@@ -349,7 +349,7 @@
         }
       }
       
-      void OnStudyReceived(const OrthancApiClient::JsonResponseReadyMessage& message)
+      void OnStudyReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message)
       {
         const Json::Value& response = message.GetJson();
 
@@ -359,13 +359,13 @@
           {
             context_->GetOrthancApiClient().GetJsonAsync(
               "/series/" + response["Series"][(int)i].asString(),
-              new Callable<SimpleViewerApplication, OrthancApiClient::JsonResponseReadyMessage>
+              new Callable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage>
               (*this, &SimpleViewerApplication::OnSeriesReceived));
           }
         }
       }
 
-      void OnSeriesReceived(const OrthancApiClient::JsonResponseReadyMessage& message)
+      void OnSeriesReceived(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message)
       {
         const Json::Value& response = message.GetJson();
 
@@ -387,7 +387,7 @@
           LoadThumbnailForSeries(seriesId, instancesIdsPerSeriesId_[seriesId][0]);
 
           // if this is the first thumbnail loaded, load the first instance in the mainWidget
-          SliceViewerWidget& widget = *dynamic_cast<SliceViewerWidget*>(mainWidget_);
+          Deprecated::SliceViewerWidget& widget = *dynamic_cast<Deprecated::SliceViewerWidget*>(mainWidget_);
           if (widget.GetLayerCount() == 0)
           {
             smartLoader_->SetFrameInWidget(widget, 0, instancesIdsPerSeriesId_[seriesId][0], 0);
@@ -398,10 +398,10 @@
       void LoadThumbnailForSeries(const std::string& seriesId, const std::string& instanceId)
       {
         LOG(INFO) << "Loading thumbnail for series " << seriesId;
-        SliceViewerWidget* thumbnailWidget = new SliceViewerWidget(GetBroker(), "thumbnail-series-" + seriesId);
+        Deprecated::SliceViewerWidget* thumbnailWidget = new Deprecated::SliceViewerWidget(GetBroker(), "thumbnail-series-" + seriesId);
         thumbnails_.push_back(thumbnailWidget);
         thumbnailsLayout_->AddWidget(thumbnailWidget);
-        thumbnailWidget->RegisterObserverCallback(new Callable<SimpleViewerApplication, SliceViewerWidget::GeometryChangedMessage>(*this, &SimpleViewerApplication::OnWidgetGeometryChanged));
+        thumbnailWidget->RegisterObserverCallback(new Callable<SimpleViewerApplication, Deprecated::SliceViewerWidget::GeometryChangedMessage>(*this, &SimpleViewerApplication::OnWidgetGeometryChanged));
         smartLoader_->SetFrameInWidget(*thumbnailWidget, 0, instanceId, 0);
         thumbnailWidget->SetInteractor(*thumbnailInteractor_);
       }
@@ -410,19 +410,19 @@
       {
         LOG(INFO) << "Selecting study: " << studyId;
         context_->GetOrthancApiClient().GetJsonAsync(
-          "/studies/" + studyId, new Callable<SimpleViewerApplication, OrthancApiClient::JsonResponseReadyMessage>
+          "/studies/" + studyId, new Callable<SimpleViewerApplication, Deprecated::OrthancApiClient::JsonResponseReadyMessage>
           (*this, &SimpleViewerApplication::OnStudyReceived));
       }
 
-      void OnWidgetGeometryChanged(const SliceViewerWidget::GeometryChangedMessage& message)
+      void OnWidgetGeometryChanged(const Deprecated::SliceViewerWidget::GeometryChangedMessage& message)
       {
         // TODO: The "const_cast" could probably be replaced by "mainWidget"
-        const_cast<SliceViewerWidget&>(message.GetOrigin()).FitContent();
+        const_cast<Deprecated::SliceViewerWidget&>(message.GetOrigin()).FitContent();
       }
 
       void SelectSeriesInMainViewport(const std::string& seriesId)
       {
-        SliceViewerWidget& widget = *dynamic_cast<SliceViewerWidget*>(mainWidget_);
+        Deprecated::SliceViewerWidget& widget = *dynamic_cast<Deprecated::SliceViewerWidget*>(mainWidget_);
         smartLoader_->SetFrameInWidget(widget, 0, instancesIdsPerSeriesId_[seriesId][0], 0);
       }
 
--- a/Applications/Samples/SingleFrameApplication.h	Wed May 22 16:01:34 2019 +0200
+++ b/Applications/Samples/SingleFrameApplication.h	Wed May 22 16:13:46 2019 +0200
@@ -23,8 +23,8 @@
 
 #include "SampleApplicationBase.h"
 
-#include "../../Framework/Layers/DicomSeriesVolumeSlicer.h"
-#include "../../Framework/Widgets/SliceViewerWidget.h"
+#include "../../Framework/Deprecated/Layers/DicomSeriesVolumeSlicer.h"
+#include "../../Framework/Deprecated/Widgets/SliceViewerWidget.h"
 
 #include <Core/Logging.h>
 #include <Core/OrthancException.h>
@@ -41,7 +41,7 @@
       public IObserver
     {
     private:
-      class Interactor : public IWorldSceneInteractor
+      class Interactor : public Deprecated::IWorldSceneInteractor
       {
       private:
         SingleFrameApplication&  application_;
@@ -52,30 +52,30 @@
         {
         }
         
-        virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget,
-                                                            const ViewportGeometry& view,
-                                                            MouseButton button,
-                                                            KeyboardModifiers modifiers,
-                                                            int viewportX,
-                                                            int viewportY,
-                                                            double x,
-                                                            double y,
-                                                            IStatusBar* statusBar,
-                                                            const std::vector<Touch>& displayTouches)
+        virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& widget,
+                                                                        const Deprecated::ViewportGeometry& view,
+                                                                        MouseButton button,
+                                                                        KeyboardModifiers modifiers,
+                                                                        int viewportX,
+                                                                        int viewportY,
+                                                                        double x,
+                                                                        double y,
+                                                                        Deprecated::IStatusBar* statusBar,
+                                                                        const std::vector<Deprecated::Touch>& displayTouches)
         {
           return NULL;
         }
 
         virtual void MouseOver(CairoContext& context,
-                               WorldSceneWidget& widget,
-                               const ViewportGeometry& view,
+                               Deprecated::WorldSceneWidget& widget,
+                               const Deprecated::ViewportGeometry& view,
                                double x,
                                double y,
-                               IStatusBar* statusBar)
+                               Deprecated::IStatusBar* statusBar)
         {
           if (statusBar != NULL)
           {
-            Vector p = dynamic_cast<SliceViewerWidget&>(widget).GetSlice().MapSliceToWorldCoordinates(x, y);
+            Vector p = dynamic_cast<Deprecated::SliceViewerWidget&>(widget).GetSlice().MapSliceToWorldCoordinates(x, y);
             
             char buf[64];
             sprintf(buf, "X = %.02f Y = %.02f Z = %.02f (in cm)", 
@@ -84,10 +84,10 @@
           }
         }
 
-        virtual void MouseWheel(WorldSceneWidget& widget,
+        virtual void MouseWheel(Deprecated::WorldSceneWidget& widget,
                                 MouseWheelDirection direction,
                                 KeyboardModifiers modifiers,
-                                IStatusBar* statusBar)
+                                Deprecated::IStatusBar* statusBar)
         {
           int scale = (modifiers & KeyboardModifiers_Control ? 10 : 1);
           
@@ -106,11 +106,11 @@
           }
         }
 
-        virtual void KeyPressed(WorldSceneWidget& widget,
+        virtual void KeyPressed(Deprecated::WorldSceneWidget& widget,
                                 KeyboardKeys key,
                                 char keyChar,
                                 KeyboardModifiers modifiers,
-                                IStatusBar* statusBar)
+                                Deprecated::IStatusBar* statusBar)
         {
           switch (keyChar)
           {
@@ -149,9 +149,9 @@
       }
 
 
-      SliceViewerWidget& GetMainWidget()
+      Deprecated::SliceViewerWidget& GetMainWidget()
       {
-        return *dynamic_cast<SliceViewerWidget*>(mainWidget_);
+        return *dynamic_cast<Deprecated::SliceViewerWidget*>(mainWidget_);
       }
       
 
@@ -184,7 +184,7 @@
       }
         
       
-      void OnMainWidgetGeometryReady(const IVolumeSlicer::GeometryReadyMessage& message)
+      void OnMainWidgetGeometryReady(const Deprecated::IVolumeSlicer::GeometryReadyMessage& message)
       {
         // Once the geometry of the series is downloaded from Orthanc,
         // display its middle slice, and adapt the viewport to fit this
@@ -198,7 +198,7 @@
       }
       
       std::auto_ptr<Interactor>         mainWidgetInteractor_;
-      const DicomSeriesVolumeSlicer*    source_;
+      const Deprecated::DicomSeriesVolumeSlicer*    source_;
       unsigned int                      slice_;
 
     public:
@@ -225,7 +225,7 @@
       }
 
       virtual void Initialize(StoneApplicationContext* context,
-                              IStatusBar& statusBar,
+                              Deprecated::IStatusBar& statusBar,
                               const boost::program_options::variables_map& parameters)
       {
         using namespace OrthancStone;
@@ -243,15 +243,15 @@
         std::string instance = parameters["instance"].as<std::string>();
         int frame = parameters["frame"].as<unsigned int>();
 
-        mainWidget_ = new SliceViewerWidget(GetBroker(), "main-widget");
+        mainWidget_ = new Deprecated::SliceViewerWidget(GetBroker(), "main-widget");
 
-        std::auto_ptr<DicomSeriesVolumeSlicer> layer(new DicomSeriesVolumeSlicer(GetBroker(), context->GetOrthancApiClient()));
+        std::auto_ptr<Deprecated::DicomSeriesVolumeSlicer> layer(new Deprecated::DicomSeriesVolumeSlicer(GetBroker(), context->GetOrthancApiClient()));
         source_ = layer.get();
         layer->LoadFrame(instance, frame);
-        layer->RegisterObserverCallback(new Callable<SingleFrameApplication, IVolumeSlicer::GeometryReadyMessage>(*this, &SingleFrameApplication::OnMainWidgetGeometryReady));
+        layer->RegisterObserverCallback(new Callable<SingleFrameApplication, Deprecated::IVolumeSlicer::GeometryReadyMessage>(*this, &SingleFrameApplication::OnMainWidgetGeometryReady));
         GetMainWidget().AddLayer(layer.release());
 
-        RenderStyle s;
+        Deprecated::RenderStyle s;
 
         if (parameters["smooth"].as<bool>())
         {
--- a/Applications/Samples/SingleFrameEditorApplication.h	Wed May 22 16:01:34 2019 +0200
+++ b/Applications/Samples/SingleFrameEditorApplication.h	Wed May 22 16:13:46 2019 +0200
@@ -52,7 +52,7 @@
   namespace Samples
   {
     class RadiographyEditorInteractor :
-        public IWorldSceneInteractor,
+        public Deprecated::IWorldSceneInteractor,
         public IObserver
     {
     private:
@@ -97,16 +97,16 @@
       {
         maskLayer_ = maskLayer;
       }
-      virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& worldWidget,
-                                                          const ViewportGeometry& view,
-                                                          MouseButton button,
-                                                          KeyboardModifiers modifiers,
-                                                          int viewportX,
-                                                          int viewportY,
-                                                          double x,
-                                                          double y,
-                                                          IStatusBar* statusBar,
-                                                          const std::vector<Touch>& displayTouches)
+      virtual Deprecated::IWorldSceneMouseTracker* CreateMouseTracker(Deprecated::WorldSceneWidget& worldWidget,
+                                                                      const Deprecated::ViewportGeometry& view,
+                                                                      MouseButton button,
+                                                                      KeyboardModifiers modifiers,
+                                                                      int viewportX,
+                                                                      int viewportY,
+                                                                      double x,
+                                                                      double y,
+                                                                      Deprecated::IStatusBar* statusBar,
+                                                                      const std::vector<Deprecated::Touch>& displayTouches)
       {
         RadiographyWidget& widget = dynamic_cast<RadiographyWidget&>(worldWidget);
 
@@ -226,11 +226,11 @@
       }
 
       virtual void MouseOver(CairoContext& context,
-                             WorldSceneWidget& worldWidget,
-                             const ViewportGeometry& view,
+                             Deprecated::WorldSceneWidget& worldWidget,
+                             const Deprecated::ViewportGeometry& view,
                              double x,
                              double y,
-                             IStatusBar* statusBar)
+                             Deprecated::IStatusBar* statusBar)
       {
         RadiographyWidget& widget = dynamic_cast<RadiographyWidget&>(worldWidget);
 
@@ -270,18 +270,18 @@
         }
       }
 
-      virtual void MouseWheel(WorldSceneWidget& widget,
+      virtual void MouseWheel(Deprecated::WorldSceneWidget& widget,
                               MouseWheelDirection direction,
                               KeyboardModifiers modifiers,
-                              IStatusBar* statusBar)
+                              Deprecated::IStatusBar* statusBar)
       {
       }
 
-      virtual void KeyPressed(WorldSceneWidget& worldWidget,
+      virtual void KeyPressed(Deprecated::WorldSceneWidget& worldWidget,
                               KeyboardKeys key,
                               char keyChar,
                               KeyboardModifiers modifiers,
-                              IStatusBar* statusBar)
+                              Deprecated::IStatusBar* statusBar)
       {
         RadiographyWidget& widget = dynamic_cast<RadiographyWidget&>(worldWidget);
 
@@ -455,7 +455,7 @@
       }
 
       virtual void Initialize(StoneApplicationContext* context,
-                              IStatusBar& statusBar,
+                              Deprecated::IStatusBar& statusBar,
                               const boost::program_options::variables_map& parameters)
       {
         using namespace OrthancStone;
@@ -485,7 +485,7 @@
         }
 
         std::string instance = parameters["instance"].as<std::string>();
-        int frame = parameters["frame"].as<unsigned int>();
+        //int frame = parameters["frame"].as<unsigned int>();
 
         fontRegistry_.AddFromResource(Orthanc::EmbeddedResources::FONT_UBUNTU_MONO_BOLD_16);
         
--- a/Applications/Sdl/SdlCairoSurface.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Applications/Sdl/SdlCairoSurface.cpp	Wed May 22 16:13:46 2019 +0200
@@ -74,7 +74,7 @@
   }
 
 
-  void SdlCairoSurface::Render(IViewport& viewport)
+  void SdlCairoSurface::Render(Deprecated::IViewport& viewport)
   {
     if (cairoSurface_.get() == NULL)
     {
--- a/Applications/Sdl/SdlCairoSurface.h	Wed May 22 16:01:34 2019 +0200
+++ b/Applications/Sdl/SdlCairoSurface.h	Wed May 22 16:13:46 2019 +0200
@@ -25,7 +25,7 @@
 
 #include "SdlWindow.h"
 #include "../../Framework/Viewport/CairoSurface.h"
-#include "../../Framework/Viewport/IViewport.h"
+#include "../../Framework/Deprecated/Viewport/IViewport.h"
 
 #include <boost/thread/mutex.hpp>
 
@@ -46,7 +46,7 @@
     void SetSize(unsigned int width,
                  unsigned int height);
 
-    void Render(IViewport& viewport);
+    void Render(Deprecated::IViewport& viewport);
   };
 }
 
--- a/Applications/Sdl/SdlEngine.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Applications/Sdl/SdlEngine.cpp	Wed May 22 16:13:46 2019 +0200
@@ -146,15 +146,15 @@
           switch (event.button.button)
           {
           case SDL_BUTTON_LEFT:
-            locker.GetCentralViewport().MouseDown(MouseButton_Left, event.button.x, event.button.y, modifiers, std::vector<Touch>());
+            locker.GetCentralViewport().MouseDown(MouseButton_Left, event.button.x, event.button.y, modifiers, std::vector<Deprecated::Touch>());
             break;
             
           case SDL_BUTTON_RIGHT:
-            locker.GetCentralViewport().MouseDown(MouseButton_Right, event.button.x, event.button.y, modifiers, std::vector<Touch>());
+            locker.GetCentralViewport().MouseDown(MouseButton_Right, event.button.x, event.button.y, modifiers, std::vector<Deprecated::Touch>());
             break;
             
           case SDL_BUTTON_MIDDLE:
-            locker.GetCentralViewport().MouseDown(MouseButton_Middle, event.button.x, event.button.y, modifiers, std::vector<Touch>());
+            locker.GetCentralViewport().MouseDown(MouseButton_Middle, event.button.x, event.button.y, modifiers, std::vector<Deprecated::Touch>());
             break;
 
           default:
@@ -163,7 +163,7 @@
         }
         else if (event.type == SDL_MOUSEMOTION)
         {
-          locker.GetCentralViewport().MouseMove(event.button.x, event.button.y, std::vector<Touch>());
+          locker.GetCentralViewport().MouseMove(event.button.x, event.button.y, std::vector<Deprecated::Touch>());
         }
         else if (event.type == SDL_MOUSEBUTTONUP)
         {
--- a/Applications/Sdl/SdlEngine.h	Wed May 22 16:01:34 2019 +0200
+++ b/Applications/Sdl/SdlEngine.h	Wed May 22 16:13:46 2019 +0200
@@ -49,7 +49,7 @@
               NativeStoneApplicationContext& context,
               MessageBroker& broker);
   
-    void OnViewportChanged(const IViewport::ViewportChangedMessage& message)
+    void OnViewportChanged(const Deprecated::IViewport::ViewportChangedMessage& message)
     {
       viewportChanged_ = true;
     }
--- a/Applications/Sdl/SdlStoneApplicationRunner.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Applications/Sdl/SdlStoneApplicationRunner.cpp	Wed May 22 16:13:46 2019 +0200
@@ -110,7 +110,7 @@
       NativeStoneApplicationContext::GlobalMutexLocker locker(context);
 
       locker.GetCentralViewport().RegisterObserverCallback(
-        new Callable<SdlEngine, IViewport::ViewportChangedMessage>
+        new Callable<SdlEngine, Deprecated::IViewport::ViewportChangedMessage>
         (sdl, &SdlEngine::OnViewportChanged));
 
       //context.GetCentralViewport().Register(sdl);  // (*)
--- a/Applications/StoneApplicationContext.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Applications/StoneApplicationContext.cpp	Wed May 22 16:13:46 2019 +0200
@@ -32,11 +32,11 @@
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
 
-    orthanc_.reset(new OrthancApiClient(broker_, *webService_, orthancBaseUrl_));
+    orthanc_.reset(new Deprecated::OrthancApiClient(broker_, *webService_, orthancBaseUrl_));
   }
 
 
-  IWebService& StoneApplicationContext::GetWebService()
+  Deprecated::IWebService& StoneApplicationContext::GetWebService()
   {
     if (webService_ == NULL)
     {
@@ -47,7 +47,7 @@
   }
 
   
-  OrthancApiClient& StoneApplicationContext::GetOrthancApiClient()
+  Deprecated::OrthancApiClient& StoneApplicationContext::GetOrthancApiClient()
   {
     if (orthanc_.get() == NULL)
     {
@@ -58,7 +58,7 @@
   }
 
   
-  void StoneApplicationContext::SetWebService(IWebService& webService)
+  void StoneApplicationContext::SetWebService(Deprecated::IWebService& webService)
   {
     webService_ = &webService;
     InitializeOrthanc();
--- a/Applications/StoneApplicationContext.h	Wed May 22 16:01:34 2019 +0200
+++ b/Applications/StoneApplicationContext.h	Wed May 22 16:13:46 2019 +0200
@@ -21,10 +21,10 @@
 
 #pragma once
 
-#include "../Framework/Toolbox/IWebService.h"
-#include "../Framework/Toolbox/IDelayedCallExecutor.h"
-#include "../Framework/Toolbox/OrthancApiClient.h"
-#include "../Framework/Viewport/WidgetViewport.h"
+#include "../Framework/Deprecated/Toolbox/IWebService.h"
+#include "../Framework/Deprecated/Toolbox/IDelayedCallExecutor.h"
+#include "../Framework/Deprecated/Toolbox/OrthancApiClient.h"
+#include "../Framework/Deprecated/Viewport/WidgetViewport.h"
 
 
 #ifdef _MSC_VER
@@ -60,9 +60,9 @@
   {
   private:
     MessageBroker&                   broker_;
-    IWebService*                     webService_;
-    IDelayedCallExecutor*            delayedCallExecutor_;
-    std::auto_ptr<OrthancApiClient>  orthanc_;
+    Deprecated::IWebService*         webService_;
+    Deprecated::IDelayedCallExecutor*            delayedCallExecutor_;
+    std::auto_ptr<Deprecated::OrthancApiClient>  orthanc_;
     std::string                      orthancBaseUrl_;
 
     void InitializeOrthanc();
@@ -89,20 +89,20 @@
       return webService_ != NULL;
     }
 
-    IWebService& GetWebService();
+    Deprecated::IWebService& GetWebService();
 
-    OrthancApiClient& GetOrthancApiClient();
+    Deprecated::OrthancApiClient& GetOrthancApiClient();
 
-    void SetWebService(IWebService& webService);
+    void SetWebService(Deprecated::IWebService& webService);
 
     void SetOrthancBaseUrl(const std::string& baseUrl);
 
-    void SetDelayedCallExecutor(IDelayedCallExecutor& delayedCallExecutor)
+    void SetDelayedCallExecutor(Deprecated::IDelayedCallExecutor& delayedCallExecutor)
     {
       delayedCallExecutor_ = &delayedCallExecutor;
     }
 
-    IDelayedCallExecutor& GetDelayedCallExecutor()
+    Deprecated::IDelayedCallExecutor& GetDelayedCallExecutor()
     {
       return *delayedCallExecutor_;
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Layers/CircleMeasureTracker.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,106 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "CircleMeasureTracker.h"
+
+#include <stdio.h>
+#include <boost/math/constants/constants.hpp>
+
+namespace Deprecated
+{
+  CircleMeasureTracker::CircleMeasureTracker(IStatusBar* statusBar,
+                                             const OrthancStone::CoordinateSystem3D& slice,
+                                             double x, 
+                                             double y,
+                                             uint8_t red,
+                                             uint8_t green,
+                                             uint8_t blue,
+                                             const Orthanc::Font& font) :
+    statusBar_(statusBar),
+    slice_(slice),
+    x1_(x),
+    y1_(y),
+    x2_(x),
+    y2_(y),
+    font_(font)
+  {
+    color_[0] = red;
+    color_[1] = green;
+    color_[2] = blue;
+  }
+    
+
+  void CircleMeasureTracker::Render(OrthancStone::CairoContext& context,
+                                    double zoom)
+  {
+    double x = (x1_ + x2_) / 2.0;
+    double y = (y1_ + y2_) / 2.0;
+
+    OrthancStone::Vector tmp;
+    OrthancStone::LinearAlgebra::AssignVector(tmp, x2_ - x1_, y2_ - y1_);
+    double r = boost::numeric::ublas::norm_2(tmp) / 2.0;
+
+    context.SetSourceColor(color_[0], color_[1], color_[2]);
+
+    cairo_t* cr = context.GetObject();
+    cairo_save(cr);
+    cairo_set_line_width(cr, 2.0 / zoom);
+    cairo_translate(cr, x, y);
+    cairo_arc(cr, 0, 0, r, 0, 2.0 * boost::math::constants::pi<double>());
+    cairo_stroke_preserve(cr);
+    cairo_stroke(cr);
+    cairo_restore(cr);
+
+    context.DrawText(font_, FormatRadius(), x, y, OrthancStone::BitmapAnchor_Center);
+  }
+    
+
+  double CircleMeasureTracker::GetRadius() const  // In millimeters
+  {
+    OrthancStone::Vector a = slice_.MapSliceToWorldCoordinates(x1_, y1_);
+    OrthancStone::Vector b = slice_.MapSliceToWorldCoordinates(x2_, y2_);
+    return boost::numeric::ublas::norm_2(b - a) / 2.0;
+  }
+
+
+  std::string CircleMeasureTracker::FormatRadius() const
+  {
+    char buf[64];
+    sprintf(buf, "%0.01f cm", GetRadius() / 10.0);
+    return buf;
+  }
+
+  void CircleMeasureTracker::MouseMove(int displayX,
+                                       int displayY,
+                                       double x,
+                                       double y,
+                                       const std::vector<Touch>& displayTouches,
+                                       const std::vector<Touch>& sceneTouches)
+  {
+    x2_ = x;
+    y2_ = y;
+
+    if (statusBar_ != NULL)
+    {
+      statusBar_->SetMessage("Circle radius: " + FormatRadius());
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Layers/CircleMeasureTracker.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,78 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../Widgets/IWorldSceneMouseTracker.h"
+#include "../Viewport/IStatusBar.h"
+#include "../../Toolbox/CoordinateSystem3D.h"
+
+#include <Core/Images/Font.h>
+
+namespace Deprecated
+{
+  class CircleMeasureTracker : public IWorldSceneMouseTracker
+  {
+  private:
+    IStatusBar*           statusBar_;
+    OrthancStone::CoordinateSystem3D    slice_;
+    double                x1_;
+    double                y1_;
+    double                x2_;
+    double                y2_;
+    uint8_t               color_[3];
+    const Orthanc::Font&  font_;
+
+  public:
+    CircleMeasureTracker(IStatusBar* statusBar,
+                         const OrthancStone::CoordinateSystem3D& slice,
+                         double x, 
+                         double y,
+                         uint8_t red,
+                         uint8_t green,
+                         uint8_t blue,
+                         const Orthanc::Font& font);
+    
+    virtual bool HasRender() const
+    {
+      return true;
+    }
+
+    virtual void Render(OrthancStone::CairoContext& context,
+                        double zoom);
+    
+    double GetRadius() const;  // In millimeters
+
+    std::string FormatRadius() const;
+
+    virtual void MouseUp()
+    {
+      // Possibly create a new landmark "volume" with the circle in subclasses
+    }
+
+    virtual void MouseMove(int displayX,
+                           int displayY,
+                           double x,
+                           double y,
+                           const std::vector<Touch>& displayTouches,
+                           const std::vector<Touch>& sceneTouches);
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Layers/ColorFrameRenderer.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,62 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "ColorFrameRenderer.h"
+
+#include <Core/OrthancException.h>
+#include <Core/Images/Image.h>
+#include <Core/Images/ImageProcessing.h>
+
+namespace Deprecated
+{
+  OrthancStone::CairoSurface* ColorFrameRenderer::GenerateDisplay(const RenderStyle& style)
+  {
+    std::auto_ptr<OrthancStone::CairoSurface> display
+      (new OrthancStone::CairoSurface(frame_->GetWidth(), frame_->GetHeight(), false /* no alpha */));
+
+    Orthanc::ImageAccessor target;
+    display->GetWriteableAccessor(target);
+    
+    Orthanc::ImageProcessing::Convert(target, *frame_);
+
+    return display.release();
+  }
+
+
+  ColorFrameRenderer::ColorFrameRenderer(const Orthanc::ImageAccessor& frame,
+                                         const OrthancStone::CoordinateSystem3D& framePlane,
+                                         double pixelSpacingX,
+                                         double pixelSpacingY,
+                                         bool isFullQuality) :
+    FrameRenderer(framePlane, pixelSpacingX, pixelSpacingY, isFullQuality),
+    frame_(Orthanc::Image::Clone(frame))
+  {
+    if (frame_.get() == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+
+    if (frame_->GetFormat() != Orthanc::PixelFormat_RGB24)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Layers/ColorFrameRenderer.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,43 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "FrameRenderer.h"
+
+namespace Deprecated
+{
+  class ColorFrameRenderer : public FrameRenderer
+  {
+  private:
+    std::auto_ptr<Orthanc::ImageAccessor>   frame_;  // In RGB24
+
+  protected:
+    virtual OrthancStone::CairoSurface* GenerateDisplay(const RenderStyle& style);
+
+  public:
+    ColorFrameRenderer(const Orthanc::ImageAccessor& frame,
+                       const OrthancStone::CoordinateSystem3D& framePlane,
+                       double pixelSpacingX,
+                       double pixelSpacingY,
+                       bool isFullQuality);
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Layers/DicomSeriesVolumeSlicer.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,162 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "DicomSeriesVolumeSlicer.h"
+
+#include "FrameRenderer.h"
+#include "../Toolbox/DicomFrameConverter.h"
+
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+
+#include <boost/lexical_cast.hpp>
+
+namespace Deprecated
+{
+
+  void DicomSeriesVolumeSlicer::OnSliceGeometryReady(const OrthancSlicesLoader::SliceGeometryReadyMessage& message)
+  {
+    if (message.GetOrigin().GetSlicesCount() > 0)
+    {
+      BroadcastMessage(IVolumeSlicer::GeometryReadyMessage(*this));
+    }
+    else
+    {
+      BroadcastMessage(IVolumeSlicer::GeometryErrorMessage(*this));
+    }
+  }
+
+  void DicomSeriesVolumeSlicer::OnSliceGeometryError(const OrthancSlicesLoader::SliceGeometryErrorMessage& message)
+  {
+    BroadcastMessage(IVolumeSlicer::GeometryErrorMessage(*this));
+  }
+
+
+  class DicomSeriesVolumeSlicer::RendererFactory : public LayerReadyMessage::IRendererFactory
+  {
+  private:
+    const OrthancSlicesLoader::SliceImageReadyMessage&  message_;
+
+  public:
+    RendererFactory(const OrthancSlicesLoader::SliceImageReadyMessage& message) :
+      message_(message)
+    {
+    }
+
+    virtual ILayerRenderer* CreateRenderer() const
+    {
+      bool isFull = (message_.GetEffectiveQuality() == SliceImageQuality_FullPng ||
+                     message_.GetEffectiveQuality() == SliceImageQuality_FullPam);
+
+      return FrameRenderer::CreateRenderer(message_.GetImage(), message_.GetSlice(), isFull);
+    }
+  };
+
+  void DicomSeriesVolumeSlicer::OnSliceImageReady(const OrthancSlicesLoader::SliceImageReadyMessage& message)
+  {
+    // first notify that the pixel data of the frame is ready (targeted to, i.e: an image cache)
+    BroadcastMessage(FrameReadyMessage(*this, message.GetImage(), 
+                                  message.GetEffectiveQuality(), message.GetSlice()));
+
+    // then notify that the layer is ready for rendering
+    RendererFactory factory(message);
+    BroadcastMessage(IVolumeSlicer::LayerReadyMessage(*this, factory, message.GetSlice().GetGeometry()));
+  }
+
+  void DicomSeriesVolumeSlicer::OnSliceImageError(const OrthancSlicesLoader::SliceImageErrorMessage& message)
+  {
+    BroadcastMessage(IVolumeSlicer::LayerErrorMessage(*this, message.GetSlice().GetGeometry()));
+  }
+
+
+  DicomSeriesVolumeSlicer::DicomSeriesVolumeSlicer(OrthancStone::MessageBroker& broker,
+                                                   OrthancApiClient& orthanc) :
+    IVolumeSlicer(broker),
+    IObserver(broker),
+    loader_(broker, orthanc),
+    quality_(SliceImageQuality_FullPng)
+  {
+    loader_.RegisterObserverCallback(
+      new OrthancStone::Callable<DicomSeriesVolumeSlicer, OrthancSlicesLoader::SliceGeometryReadyMessage>
+        (*this, &DicomSeriesVolumeSlicer::OnSliceGeometryReady));
+
+    loader_.RegisterObserverCallback(
+      new OrthancStone::Callable<DicomSeriesVolumeSlicer, OrthancSlicesLoader::SliceGeometryErrorMessage>
+      (*this, &DicomSeriesVolumeSlicer::OnSliceGeometryError));
+
+    loader_.RegisterObserverCallback(
+      new OrthancStone::Callable<DicomSeriesVolumeSlicer, OrthancSlicesLoader::SliceImageReadyMessage>
+        (*this, &DicomSeriesVolumeSlicer::OnSliceImageReady));
+
+    loader_.RegisterObserverCallback(
+      new OrthancStone::Callable<DicomSeriesVolumeSlicer, OrthancSlicesLoader::SliceImageErrorMessage>
+      (*this, &DicomSeriesVolumeSlicer::OnSliceImageError));
+  }
+
+  
+  void DicomSeriesVolumeSlicer::LoadSeries(const std::string& seriesId)
+  {
+    loader_.ScheduleLoadSeries(seriesId);
+  }
+
+
+  void DicomSeriesVolumeSlicer::LoadInstance(const std::string& instanceId)
+  {
+    loader_.ScheduleLoadInstance(instanceId);
+  }
+
+
+  void DicomSeriesVolumeSlicer::LoadFrame(const std::string& instanceId,
+                                          unsigned int frame)
+  {
+    loader_.ScheduleLoadFrame(instanceId, frame);
+  }
+
+
+  bool DicomSeriesVolumeSlicer::GetExtent(std::vector<OrthancStone::Vector>& points,
+                                          const OrthancStone::CoordinateSystem3D& viewportSlice)
+  {
+    size_t index;
+
+    if (loader_.IsGeometryReady() &&
+        loader_.LookupSlice(index, viewportSlice))
+    {
+      loader_.GetSlice(index).GetExtent(points);
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+  
+  void DicomSeriesVolumeSlicer::ScheduleLayerCreation(const OrthancStone::CoordinateSystem3D& viewportSlice)
+  {
+    size_t index;
+
+    if (loader_.IsGeometryReady() &&
+        loader_.LookupSlice(index, viewportSlice))
+    {
+      loader_.ScheduleLoadSliceImage(index, quality_);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Layers/DicomSeriesVolumeSlicer.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,127 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "IVolumeSlicer.h"
+#include "../Toolbox/IWebService.h"
+#include "../Toolbox/OrthancSlicesLoader.h"
+#include "../Toolbox/OrthancApiClient.h"
+
+namespace Deprecated
+{  
+  // this class is in charge of loading a Frame.
+  // once it's been loaded (first the geometry and then the image),
+  // messages are sent to observers so they can use it
+  class DicomSeriesVolumeSlicer :
+    public IVolumeSlicer,
+    public OrthancStone::IObserver
+    //private OrthancSlicesLoader::ISliceLoaderObserver
+  {
+  public:
+    // TODO: Add "frame" and "instanceId"
+    class FrameReadyMessage : public OrthancStone::OriginMessage<DicomSeriesVolumeSlicer>
+    {
+      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
+      
+    private:
+      const Orthanc::ImageAccessor&  frame_;
+      SliceImageQuality              imageQuality_;
+      const Slice&                   slice_;
+
+    public:
+      FrameReadyMessage(DicomSeriesVolumeSlicer& origin,
+                        const Orthanc::ImageAccessor& frame,
+                        SliceImageQuality imageQuality,
+                        const Slice& slice) :
+        OriginMessage(origin),
+        frame_(frame),
+        imageQuality_(imageQuality),
+        slice_(slice)
+      {
+      }
+
+      const Orthanc::ImageAccessor& GetFrame() const
+      {
+        return frame_;
+      }
+
+      SliceImageQuality GetImageQuality() const
+      {
+        return imageQuality_;
+      }
+
+      const Slice& GetSlice() const
+      {
+        return slice_;
+      }
+    };
+
+    
+  private:
+    class RendererFactory;
+    
+    OrthancSlicesLoader  loader_;
+    SliceImageQuality    quality_;
+
+  public:
+    DicomSeriesVolumeSlicer(OrthancStone::MessageBroker& broker,
+                            OrthancApiClient& orthanc);
+
+    void LoadSeries(const std::string& seriesId);
+
+    void LoadInstance(const std::string& instanceId);
+
+    void LoadFrame(const std::string& instanceId,
+                   unsigned int frame);
+
+    void SetImageQuality(SliceImageQuality quality)
+    {
+      quality_ = quality;
+    }
+
+    SliceImageQuality GetImageQuality() const
+    {
+      return quality_;
+    }
+
+    size_t GetSlicesCount() const
+    {
+      return loader_.GetSlicesCount();
+    }
+
+    const Slice& GetSlice(size_t slice) const 
+    {
+      return loader_.GetSlice(slice);
+    }
+
+    virtual bool GetExtent(std::vector<OrthancStone::Vector>& points,
+                           const OrthancStone::CoordinateSystem3D& viewportSlice);
+
+    virtual void ScheduleLayerCreation(const OrthancStone::CoordinateSystem3D& viewportSlice);
+
+protected:
+    void OnSliceGeometryReady(const OrthancSlicesLoader::SliceGeometryReadyMessage& message);
+    void OnSliceGeometryError(const OrthancSlicesLoader::SliceGeometryErrorMessage& message);
+    void OnSliceImageReady(const OrthancSlicesLoader::SliceImageReadyMessage& message);
+    void OnSliceImageError(const OrthancSlicesLoader::SliceImageErrorMessage& message);
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Layers/DicomStructureSetSlicer.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,170 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "DicomStructureSetSlicer.h"
+
+namespace Deprecated
+{
+  class DicomStructureSetSlicer::Renderer : public ILayerRenderer
+  {
+  private:
+    class Structure
+    {
+    private:
+      bool                                                         visible_;
+      uint8_t                                                      red_;
+      uint8_t                                                      green_;
+      uint8_t                                                      blue_;
+      std::string                                                  name_;
+      std::vector< std::vector<OrthancStone::DicomStructureSet::PolygonPoint> >  polygons_;
+
+    public:
+      Structure(OrthancStone::DicomStructureSet& structureSet,
+                const OrthancStone::CoordinateSystem3D& plane,
+                size_t index) :
+        name_(structureSet.GetStructureName(index))
+      {
+        structureSet.GetStructureColor(red_, green_, blue_, index);
+        visible_ = structureSet.ProjectStructure(polygons_, index, plane);
+      }
+
+      void Render(OrthancStone::CairoContext& context)
+      {
+        if (visible_)
+        {
+          cairo_t* cr = context.GetObject();
+        
+          context.SetSourceColor(red_, green_, blue_);
+
+          for (size_t i = 0; i < polygons_.size(); i++)
+          {
+            cairo_move_to(cr, polygons_[i][0].first, polygons_[i][0].second);
+
+            for (size_t j = 1; j < polygons_[i].size(); j++)
+            {
+              cairo_line_to(cr, polygons_[i][j].first, polygons_[i][j].second);
+            }
+
+            cairo_line_to(cr, polygons_[i][0].first, polygons_[i][0].second);
+            cairo_stroke(cr);
+          }
+        }
+      }
+    };
+
+    typedef std::list<Structure*>  Structures;
+    
+    OrthancStone::CoordinateSystem3D  plane_;
+    Structures          structures_;
+    
+  public:
+    Renderer(OrthancStone::DicomStructureSet& structureSet,
+             const OrthancStone::CoordinateSystem3D& plane) :
+      plane_(plane)
+    {
+      for (size_t k = 0; k < structureSet.GetStructureCount(); k++)
+      {
+        structures_.push_back(new Structure(structureSet, plane, k));
+      }
+    }
+
+    virtual ~Renderer()
+    {
+      for (Structures::iterator it = structures_.begin();
+           it != structures_.end(); ++it)
+      {
+        delete *it;
+      }
+    }
+
+    virtual bool RenderLayer(OrthancStone::CairoContext& context,
+                             const ViewportGeometry& view)
+    {
+      cairo_set_line_width(context.GetObject(), 2.0f / view.GetZoom());
+
+      for (Structures::const_iterator it = structures_.begin();
+           it != structures_.end(); ++it)
+      {
+        assert(*it != NULL);
+        (*it)->Render(context);
+      }
+
+      return true;
+    }
+
+    virtual const OrthancStone::CoordinateSystem3D& GetLayerPlane()
+    {
+      return plane_;
+    }
+
+    virtual void SetLayerStyle(const RenderStyle& style)
+    {
+    }
+    
+    virtual bool IsFullQuality()
+    {
+      return true;
+    }
+  };
+
+
+  class DicomStructureSetSlicer::RendererFactory : public LayerReadyMessage::IRendererFactory
+  {
+  private:
+    OrthancStone::DicomStructureSet&         structureSet_;
+    const OrthancStone::CoordinateSystem3D&  plane_;
+
+  public:
+    RendererFactory(OrthancStone::DicomStructureSet& structureSet,
+                    const OrthancStone::CoordinateSystem3D&  plane) :
+      structureSet_(structureSet),
+      plane_(plane)
+    {
+    }
+
+    virtual ILayerRenderer* CreateRenderer() const
+    {
+      return new Renderer(structureSet_, plane_);
+    }
+  };
+  
+
+  DicomStructureSetSlicer::DicomStructureSetSlicer(OrthancStone::MessageBroker& broker,
+                                                   StructureSetLoader& loader) :
+    IVolumeSlicer(broker),
+    IObserver(broker),
+    loader_(loader)
+  {
+    loader_.RegisterObserverCallback(
+      new OrthancStone::Callable<DicomStructureSetSlicer, StructureSetLoader::ContentChangedMessage>
+      (*this, &DicomStructureSetSlicer::OnStructureSetLoaded));
+  }
+
+
+  void DicomStructureSetSlicer::ScheduleLayerCreation(const OrthancStone::CoordinateSystem3D& viewportPlane)
+  {
+    if (loader_.HasStructureSet())
+    {
+      RendererFactory factory(loader_.GetStructureSet(), viewportPlane);
+      BroadcastMessage(IVolumeSlicer::LayerReadyMessage(*this, factory, viewportPlane));
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Layers/DicomStructureSetSlicer.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,56 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "IVolumeSlicer.h"
+#include "../Volumes/StructureSetLoader.h"
+
+namespace Deprecated
+{
+  class DicomStructureSetSlicer :
+    public IVolumeSlicer,
+    public OrthancStone::IObserver
+  {
+  private:
+    class Renderer;
+    class RendererFactory;
+
+    StructureSetLoader& loader_;
+
+    void OnStructureSetLoaded(const IVolumeLoader::ContentChangedMessage& message)
+    {
+      BroadcastMessage(IVolumeSlicer::ContentChangedMessage(*this));
+    }
+
+  public:
+    DicomStructureSetSlicer(OrthancStone::MessageBroker& broker,
+                            StructureSetLoader& loader);
+
+    virtual bool GetExtent(std::vector<OrthancStone::Vector>& points,
+                           const OrthancStone::CoordinateSystem3D& viewportPlane)
+    {
+      return false;
+    }
+
+    virtual void ScheduleLayerCreation(const OrthancStone::CoordinateSystem3D& viewportPlane);
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Layers/FrameRenderer.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,140 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "FrameRenderer.h"
+
+#include "GrayscaleFrameRenderer.h"
+#include "ColorFrameRenderer.h"
+
+#include <Core/OrthancException.h>
+
+namespace Deprecated
+{
+  FrameRenderer::FrameRenderer(const OrthancStone::CoordinateSystem3D& framePlane,
+                               double pixelSpacingX,
+                               double pixelSpacingY,
+                               bool isFullQuality) :
+    framePlane_(framePlane),
+    pixelSpacingX_(pixelSpacingX),
+    pixelSpacingY_(pixelSpacingY),
+    isFullQuality_(isFullQuality)
+  {
+  }
+
+
+  bool FrameRenderer::RenderLayer(OrthancStone::CairoContext& context,
+                                  const ViewportGeometry& view)
+  {    
+    if (!style_.visible_)
+    {
+      return true;
+    }
+
+    if (display_.get() == NULL)
+    {
+      display_.reset(GenerateDisplay(style_));
+    }
+
+    assert(display_.get() != NULL);
+
+    cairo_t *cr = context.GetObject();
+
+    cairo_save(cr);
+
+    cairo_matrix_t transform;
+    cairo_matrix_init_identity(&transform);
+    cairo_matrix_scale(&transform, pixelSpacingX_, pixelSpacingY_);
+    cairo_matrix_translate(&transform, -0.5, -0.5);
+    cairo_transform(cr, &transform);
+
+    //cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+    cairo_set_source_surface(cr, display_->GetObject(), 0, 0);
+
+    switch (style_.interpolation_)
+    {
+      case OrthancStone::ImageInterpolation_Nearest:
+        cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST);
+        break;
+
+      case OrthancStone::ImageInterpolation_Bilinear:
+        cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_BILINEAR);
+        break;
+
+      default:
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+
+    cairo_paint_with_alpha(cr, style_.alpha_);
+
+    if (style_.drawGrid_)
+    {
+      context.SetSourceColor(style_.drawColor_);
+      cairo_set_line_width(cr, 0.5 / view.GetZoom());
+
+      for (unsigned int x = 0; x <= display_->GetWidth(); x++)
+      {
+        cairo_move_to(cr, x, 0);
+        cairo_line_to(cr, x, display_->GetHeight());
+      }
+
+      for (unsigned int y = 0; y <= display_->GetHeight(); y++)
+      {
+        cairo_move_to(cr, 0, y);
+        cairo_line_to(cr, display_->GetWidth(), y);
+      }
+
+      cairo_stroke(cr);
+    }
+
+    cairo_restore(cr);
+
+    return true;
+  }
+
+
+  void FrameRenderer::SetLayerStyle(const RenderStyle& style)
+  {
+    style_ = style;
+    display_.reset(NULL);
+  }
+
+
+  ILayerRenderer* FrameRenderer::CreateRenderer(const Orthanc::ImageAccessor& frame,
+                                                const Deprecated::Slice& framePlane,
+                                                bool isFullQuality)
+  {
+    if (frame.GetFormat() == Orthanc::PixelFormat_RGB24)
+    {
+      return new ColorFrameRenderer(frame,
+                                    framePlane.GetGeometry(), 
+                                    framePlane.GetPixelSpacingX(),
+                                    framePlane.GetPixelSpacingY(), isFullQuality);
+    }
+    else
+    {
+      return new GrayscaleFrameRenderer(frame,
+                                        framePlane.GetConverter(),
+                                        framePlane.GetGeometry(), 
+                                        framePlane.GetPixelSpacingX(),
+                                        framePlane.GetPixelSpacingY(), isFullQuality);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Layers/FrameRenderer.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,69 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "ILayerRenderer.h"
+
+#include "../Toolbox/Slice.h"
+
+namespace Deprecated
+{
+  class FrameRenderer : public ILayerRenderer
+  {
+  private:
+    OrthancStone::CoordinateSystem3D            framePlane_;
+    double                        pixelSpacingX_;
+    double                        pixelSpacingY_;
+    RenderStyle                   style_;
+    bool                          isFullQuality_;
+    std::auto_ptr<OrthancStone::CairoSurface>   display_;
+
+  protected:
+    virtual OrthancStone::CairoSurface* GenerateDisplay(const RenderStyle& style) = 0;
+
+  public:
+    FrameRenderer(const OrthancStone::CoordinateSystem3D& framePlane,
+                  double pixelSpacingX,
+                  double pixelSpacingY,
+                  bool isFullQuality);
+
+    virtual bool RenderLayer(OrthancStone::CairoContext& context,
+                             const ViewportGeometry& view);
+
+    virtual const OrthancStone::CoordinateSystem3D& GetLayerPlane()
+    {
+      return framePlane_;
+    }
+
+    virtual void SetLayerStyle(const RenderStyle& style);
+
+    virtual bool IsFullQuality() 
+    {
+      return isFullQuality_;
+    }
+
+    // TODO: Avoid cloning the "frame"
+    static ILayerRenderer* CreateRenderer(const Orthanc::ImageAccessor& frame,
+                                          const Deprecated::Slice& framePlane,
+                                          bool isFullQuality);
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Layers/GrayscaleFrameRenderer.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,141 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "GrayscaleFrameRenderer.h"
+
+#include <Core/Images/Image.h>
+#include <Core/OrthancException.h>
+
+namespace Deprecated
+{
+  OrthancStone::CairoSurface* GrayscaleFrameRenderer::GenerateDisplay(const RenderStyle& style)
+  {
+    assert(frame_->GetFormat() == Orthanc::PixelFormat_Float32);
+
+    std::auto_ptr<OrthancStone::CairoSurface> result;
+
+    float windowCenter, windowWidth;
+    style.ComputeWindowing(windowCenter, windowWidth,
+                           defaultWindowCenter_, defaultWindowWidth_);
+
+    float x0 = windowCenter - windowWidth / 2.0f;
+    float x1 = windowCenter + windowWidth / 2.0f;
+
+    //LOG(INFO) << "Window: " << x0 << " => " << x1;
+
+    result.reset(new OrthancStone::CairoSurface(frame_->GetWidth(), frame_->GetHeight(), false /* no alpha */));
+
+    const uint8_t* lut = NULL;
+    if (style.applyLut_)
+    {
+      if (Orthanc::EmbeddedResources::GetFileResourceSize(style.lut_) != 3 * 256)
+      {
+        // Invalid colormap
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      }
+
+      lut = reinterpret_cast<const uint8_t*>(Orthanc::EmbeddedResources::GetFileResourceBuffer(style.lut_));
+    }
+
+    Orthanc::ImageAccessor target;
+    result->GetWriteableAccessor(target);
+    
+    const unsigned int width = target.GetWidth();
+    const unsigned int height = target.GetHeight();
+    
+    for (unsigned int y = 0; y < height; y++)
+    {
+      const float* p = reinterpret_cast<const float*>(frame_->GetConstRow(y));
+      uint8_t* q = reinterpret_cast<uint8_t*>(target.GetRow(y));
+
+      for (unsigned int x = 0; x < width; x++, p++, q += 4)
+      {
+        uint8_t v = 0;
+        if (windowWidth >= 0.001f)  // Avoid division by zero
+        {
+          if (*p >= x1)
+          {
+            v = 255;
+          }
+          else if (*p <= x0)
+          {
+            v = 0;
+          }
+          else
+          {
+            // https://en.wikipedia.org/wiki/Linear_interpolation
+            v = static_cast<uint8_t>(255.0f * (*p - x0) / (x1 - x0));
+          }
+
+          if (style.reverse_ ^ (photometric_ == Orthanc::PhotometricInterpretation_Monochrome1))
+          {
+            v = 255 - v;
+          }
+        }
+
+        if (style.applyLut_)
+        {
+          assert(lut != NULL);
+          q[3] = 255;
+          q[2] = lut[3 * v];
+          q[1] = lut[3 * v + 1];
+          q[0] = lut[3 * v + 2];
+        }
+        else
+        {
+          q[3] = 255;
+          q[2] = v;
+          q[1] = v;
+          q[0] = v;
+        }
+      }
+    }
+
+    return result.release();
+  }
+
+
+  GrayscaleFrameRenderer::GrayscaleFrameRenderer(const Orthanc::ImageAccessor& frame,
+                                                 const Deprecated::DicomFrameConverter& converter,
+                                                 const OrthancStone::CoordinateSystem3D& framePlane,
+                                                 double pixelSpacingX,
+                                                 double pixelSpacingY,
+                                                 bool isFullQuality) :
+    FrameRenderer(framePlane, pixelSpacingX, pixelSpacingY, isFullQuality),
+    frame_(Orthanc::Image::Clone(frame)),
+    defaultWindowCenter_(static_cast<float>(converter.GetDefaultWindowCenter())),
+    defaultWindowWidth_(static_cast<float>(converter.GetDefaultWindowWidth())),
+    photometric_(converter.GetPhotometricInterpretation())
+  {
+    if (frame_.get() == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+
+    converter.ConvertFrameInplace(frame_);
+    assert(frame_.get() != NULL);
+
+    if (frame_->GetFormat() != Orthanc::PixelFormat_Float32)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Layers/GrayscaleFrameRenderer.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,48 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "FrameRenderer.h"
+#include "../Toolbox/DicomFrameConverter.h"
+
+namespace Deprecated
+{
+  class GrayscaleFrameRenderer : public FrameRenderer
+  {
+  private:
+    std::auto_ptr<Orthanc::ImageAccessor>   frame_;  // In Float32
+    float                                   defaultWindowCenter_;
+    float                                   defaultWindowWidth_;
+    Orthanc::PhotometricInterpretation      photometric_;
+
+  protected:
+    virtual OrthancStone::CairoSurface* GenerateDisplay(const RenderStyle& style);
+
+  public:
+    GrayscaleFrameRenderer(const Orthanc::ImageAccessor& frame,
+                           const Deprecated::DicomFrameConverter& converter,
+                           const OrthancStone::CoordinateSystem3D& framePlane,
+                           double pixelSpacingX,
+                           double pixelSpacingY,
+                           bool isFullQuality);
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Layers/ILayerRenderer.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,47 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../../Viewport/CairoContext.h"
+#include "../../Toolbox/CoordinateSystem3D.h"
+#include "../Toolbox/ViewportGeometry.h"
+#include "RenderStyle.h"
+
+namespace Deprecated
+{
+  class ILayerRenderer : public boost::noncopyable
+  {
+  public:
+    virtual ~ILayerRenderer()
+    {
+    }
+    
+    virtual bool RenderLayer(OrthancStone::CairoContext& context,
+                             const ViewportGeometry& view) = 0;
+
+    virtual void SetLayerStyle(const RenderStyle& style) = 0;
+
+    virtual const OrthancStone::CoordinateSystem3D& GetLayerPlane() = 0;
+    
+    virtual bool IsFullQuality() = 0;
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Layers/IVolumeSlicer.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,139 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "ILayerRenderer.h"
+#include "../Toolbox/Slice.h"
+#include "../../Messages/IObservable.h"
+#include "../../Messages/IMessage.h"
+#include "Core/Images/Image.h"
+#include <boost/shared_ptr.hpp>
+
+namespace Deprecated
+{
+  class IVolumeSlicer : public OrthancStone::IObservable
+  {
+  public:
+    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryReadyMessage, IVolumeSlicer);
+    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryErrorMessage, IVolumeSlicer);
+    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, ContentChangedMessage, IVolumeSlicer);
+
+    class SliceContentChangedMessage : public OrthancStone::OriginMessage<IVolumeSlicer>
+    {
+      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
+      
+    private:
+      const Deprecated::Slice& slice_;
+
+    public:
+      SliceContentChangedMessage(IVolumeSlicer& origin,
+                                 const Deprecated::Slice& slice) :
+        OriginMessage(origin),
+        slice_(slice)
+      {
+      }
+
+      const Deprecated::Slice& GetSlice() const
+      {
+        return slice_;
+      }
+    };
+    
+
+    class LayerReadyMessage : public OrthancStone::OriginMessage<IVolumeSlicer>
+    {
+      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
+      
+    public:
+      class IRendererFactory : public boost::noncopyable
+      {
+      public:
+        virtual ~IRendererFactory()
+        {
+        }
+
+        virtual ILayerRenderer* CreateRenderer() const = 0;
+      };
+    
+    private:
+      const IRendererFactory&    factory_;
+      const OrthancStone::CoordinateSystem3D&  slice_;
+
+    public:
+      LayerReadyMessage(IVolumeSlicer& origin,
+                        const IRendererFactory& rendererFactory,
+                        const OrthancStone::CoordinateSystem3D& slice) :
+        OriginMessage(origin),
+        factory_(rendererFactory),
+        slice_(slice)
+      {
+      }
+
+      ILayerRenderer* CreateRenderer() const
+      {
+        return factory_.CreateRenderer();
+      }
+
+      const OrthancStone::CoordinateSystem3D& GetSlice() const
+      {
+        return slice_;
+      }
+    };
+
+
+    class LayerErrorMessage : public OrthancStone::OriginMessage<IVolumeSlicer>
+    {
+      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
+      
+    private:
+      const OrthancStone::CoordinateSystem3D&  slice_;
+
+    public:
+      LayerErrorMessage(IVolumeSlicer& origin,
+                        const OrthancStone::CoordinateSystem3D& slice) :
+        OriginMessage(origin),
+        slice_(slice)
+      {
+      }
+
+      const OrthancStone::CoordinateSystem3D& GetSlice() const
+      {
+        return slice_;
+      }
+    };
+
+
+    IVolumeSlicer(OrthancStone::MessageBroker& broker) :
+      IObservable(broker)
+    {
+    }
+    
+    virtual ~IVolumeSlicer()
+    {
+    }
+
+    virtual bool GetExtent(std::vector<OrthancStone::Vector>& points,
+                           const OrthancStone::CoordinateSystem3D& viewportSlice) = 0;
+
+    virtual void ScheduleLayerCreation(const OrthancStone::CoordinateSystem3D& viewportSlice) = 0;
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Layers/LineLayerRenderer.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,67 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "LineLayerRenderer.h"
+
+namespace Deprecated
+{
+  LineLayerRenderer::LineLayerRenderer(double x1,
+                                       double y1,
+                                       double x2,
+                                       double y2,
+                                       const OrthancStone::CoordinateSystem3D& plane) : 
+    x1_(x1),
+    y1_(y1),
+    x2_(x2),
+    y2_(y2),
+    plane_(plane)
+  {
+    RenderStyle style;
+    SetLayerStyle(style);
+  }
+
+
+  bool LineLayerRenderer::RenderLayer(OrthancStone::CairoContext& context,
+                                      const ViewportGeometry& view)
+  {
+    if (visible_)
+    {
+      context.SetSourceColor(color_);
+
+      cairo_t *cr = context.GetObject();
+      cairo_set_line_width(cr, 1.0 / view.GetZoom());
+      cairo_move_to(cr, x1_, y1_);
+      cairo_line_to(cr, x2_, y2_);
+      cairo_stroke(cr);
+    }
+
+    return true;
+  }
+
+
+  void LineLayerRenderer::SetLayerStyle(const RenderStyle& style)
+  {
+    visible_ = style.visible_;
+    color_[0] = style.drawColor_[0];
+    color_[1] = style.drawColor_[1];
+    color_[2] = style.drawColor_[2];
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Layers/LineLayerRenderer.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,61 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "ILayerRenderer.h"
+
+namespace Deprecated
+{
+  class LineLayerRenderer : public ILayerRenderer
+  {
+  private:
+    double              x1_;
+    double              y1_;
+    double              x2_;
+    double              y2_;
+    OrthancStone::CoordinateSystem3D  plane_;
+    bool                visible_;
+    uint8_t             color_[3];
+
+  public:
+    LineLayerRenderer(double x1,
+                      double y1,
+                      double x2,
+                      double y2,
+                      const OrthancStone::CoordinateSystem3D& plane);
+
+    virtual bool RenderLayer(OrthancStone::CairoContext& context,
+                             const ViewportGeometry& view);
+
+    virtual void SetLayerStyle(const RenderStyle& style);
+
+    virtual const OrthancStone::CoordinateSystem3D& GetLayerPlane()
+    {
+      return plane_;
+    }
+    
+    virtual bool IsFullQuality()
+    {
+      return true;
+    }
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Layers/LineMeasureTracker.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,102 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "LineMeasureTracker.h"
+
+#include <stdio.h>
+
+namespace Deprecated
+{
+  LineMeasureTracker::LineMeasureTracker(IStatusBar* statusBar,
+                                         const OrthancStone::CoordinateSystem3D& slice,
+                                         double x, 
+                                         double y,
+                                         uint8_t red,
+                                         uint8_t green,
+                                         uint8_t blue,
+                                         const Orthanc::Font& font) :
+    statusBar_(statusBar),
+    slice_(slice),
+    x1_(x),
+    y1_(y),
+    x2_(x),
+    y2_(y),
+    font_(font)
+  {
+    color_[0] = red;
+    color_[1] = green;
+    color_[2] = blue;
+  }
+    
+
+  void LineMeasureTracker::Render(OrthancStone::CairoContext& context,
+                                  double zoom)
+  {
+    context.SetSourceColor(color_[0], color_[1], color_[2]);
+
+    cairo_t* cr = context.GetObject();
+    cairo_set_line_width(cr, 2.0 / zoom);
+    cairo_move_to(cr, x1_, y1_);
+    cairo_line_to(cr, x2_, y2_);
+    cairo_stroke(cr);
+
+    if (y2_ - y1_ < 0)
+    {
+      context.DrawText(font_, FormatLength(), x2_, y2_ - 5, OrthancStone::BitmapAnchor_BottomCenter);
+    }
+    else
+    {
+      context.DrawText(font_, FormatLength(), x2_, y2_ + 5, OrthancStone::BitmapAnchor_TopCenter);
+    }
+  }
+    
+
+  double LineMeasureTracker::GetLength() const  // In millimeters
+  {
+    OrthancStone::Vector a = slice_.MapSliceToWorldCoordinates(x1_, y1_);
+    OrthancStone::Vector b = slice_.MapSliceToWorldCoordinates(x2_, y2_);
+    return boost::numeric::ublas::norm_2(b - a);
+  }
+
+
+  std::string LineMeasureTracker::FormatLength() const
+  {
+    char buf[64];
+    sprintf(buf, "%0.01f cm", GetLength() / 10.0);
+    return buf;
+  }
+
+  void LineMeasureTracker::MouseMove(int displayX,
+                                     int displayY,
+                                     double x,
+                                     double y,
+                                     const std::vector<Touch>& displayTouches,
+                                     const std::vector<Touch>& sceneTouches)
+  {
+    x2_ = x;
+    y2_ = y;
+
+    if (statusBar_ != NULL)
+    {
+      statusBar_->SetMessage("Line length: " + FormatLength());
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Layers/LineMeasureTracker.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,78 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../Widgets/IWorldSceneMouseTracker.h"
+
+#include "../Viewport/IStatusBar.h"
+#include "../../Toolbox/CoordinateSystem3D.h"
+
+namespace Deprecated
+{
+  class LineMeasureTracker : public IWorldSceneMouseTracker
+  {
+  private:
+    IStatusBar*           statusBar_;
+    OrthancStone::CoordinateSystem3D    slice_;
+    double                x1_;
+    double                y1_;
+    double                x2_;
+    double                y2_;
+    uint8_t               color_[3];
+    unsigned int          fontSize_;
+    const Orthanc::Font&  font_;
+
+  public:
+    LineMeasureTracker(IStatusBar* statusBar,
+                       const OrthancStone::CoordinateSystem3D& slice,
+                       double x, 
+                       double y,
+                       uint8_t red,
+                       uint8_t green,
+                       uint8_t blue,
+                       const Orthanc::Font& font);
+
+    virtual bool HasRender() const
+    {
+      return true;
+    }
+
+    virtual void Render(OrthancStone::CairoContext& context,
+                        double zoom);
+    
+    double GetLength() const;  // In millimeters
+
+    std::string FormatLength() const;
+
+    virtual void MouseUp()
+    {
+      // Possibly create a new landmark "volume" with the line in subclasses
+    }
+
+    virtual void MouseMove(int displayX,
+                           int displayY,
+                           double x,
+                           double y,
+                           const std::vector<Touch>& displayTouches,
+                           const std::vector<Touch>& sceneTouches);
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Layers/RenderStyle.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,106 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "RenderStyle.h"
+
+#include "../../Volumes/ImageBuffer3D.h"
+#include "../Toolbox/DicomFrameConverter.h"
+
+#include <Core/OrthancException.h>
+
+namespace Deprecated
+{
+  RenderStyle::RenderStyle()
+  {
+    visible_ = true;
+    reverse_ = false;
+    windowing_ = OrthancStone::ImageWindowing_Custom;
+    alpha_ = 1;
+    applyLut_ = false;
+    lut_ = Orthanc::EmbeddedResources::COLORMAP_HOT;
+    drawGrid_ = false;
+    drawColor_[0] = 255;
+    drawColor_[1] = 255;
+    drawColor_[2] = 255;
+    customWindowCenter_ = 128;
+    customWindowWidth_ = 256;
+    interpolation_ = OrthancStone::ImageInterpolation_Nearest;
+    fontSize_ = 14;
+  }
+
+
+  void RenderStyle::ComputeWindowing(float& targetCenter,
+                                     float& targetWidth,
+                                     float defaultCenter,
+                                     float defaultWidth) const
+  {
+    if (windowing_ == OrthancStone::ImageWindowing_Custom)
+    {
+      targetCenter = customWindowCenter_;
+      targetWidth = customWindowWidth_;
+    }
+    else
+    {
+      return ::OrthancStone::ComputeWindowing
+        (targetCenter, targetWidth, windowing_, defaultCenter, defaultWidth);
+    }
+  }
+
+  
+  void RenderStyle::SetColor(uint8_t red,
+                             uint8_t green,
+                             uint8_t blue)
+  {
+    drawColor_[0] = red;
+    drawColor_[1] = green;
+    drawColor_[2] = blue;
+  }
+
+
+  bool RenderStyle::FitRange(const OrthancStone::ImageBuffer3D& image,
+                             const DicomFrameConverter& converter)
+  {
+    float minValue, maxValue;
+
+    windowing_ = OrthancStone::ImageWindowing_Custom;
+
+    if (image.GetRange(minValue, maxValue))
+    {  
+      // casting the narrower type to wider before calling the + operator
+      // will prevent overflowing (this is why the cast to double is only 
+      // done on the first operand)
+      customWindowCenter_ = static_cast<float>(
+        converter.Apply((static_cast<double>(minValue) + maxValue) / 2.0));
+      
+      customWindowWidth_ = static_cast<float>(
+        converter.Apply(static_cast<double>(maxValue) - minValue));
+      
+      if (customWindowWidth_ > 1)
+      {
+        return true;
+      }
+    }
+
+    customWindowCenter_ = 128.0;
+    customWindowWidth_ = 256.0;
+    return false;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Layers/RenderStyle.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,63 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../../StoneEnumerations.h"
+#include "../../Volumes/ImageBuffer3D.h"
+#include "../Toolbox/DicomFrameConverter.h"
+
+#include <EmbeddedResources.h>
+
+#include <stdint.h>
+
+namespace Deprecated
+{
+  struct RenderStyle
+  {
+    bool visible_;
+    bool reverse_;
+    OrthancStone::ImageWindowing windowing_;
+    float alpha_;   // In [0,1]
+    bool applyLut_;
+    Orthanc::EmbeddedResources::FileResourceId  lut_;
+    bool drawGrid_;
+    uint8_t drawColor_[3];
+    float customWindowCenter_;
+    float customWindowWidth_;
+    OrthancStone::ImageInterpolation interpolation_;
+    unsigned int fontSize_;
+    
+    RenderStyle();
+
+    void ComputeWindowing(float& targetCenter,
+                          float& targetWidth,
+                          float defaultCenter,
+                          float defaultWidth) const;
+
+    void SetColor(uint8_t red,
+                  uint8_t green,
+                  uint8_t blue);
+
+    bool FitRange(const OrthancStone::ImageBuffer3D& image,
+                  const DicomFrameConverter& converter);
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Layers/SeriesFrameRendererFactory.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,177 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "SeriesFrameRendererFactory.h"
+
+#include "FrameRenderer.h"
+
+#include <OrthancException.h>
+#include <Logging.h>
+#include <Toolbox.h>
+#include <Plugins/Samples/Common/OrthancPluginException.h>
+#include <Plugins/Samples/Common/DicomDatasetReader.h>
+
+
+namespace Deprecated
+{
+  void SeriesFrameRendererFactory::ReadCurrentFrameDataset(size_t frame)
+  {
+    if (currentDataset_.get() != NULL &&
+        (fast_ || currentFrame_ == frame))
+    {
+      // The frame has not changed since the previous call, no need to
+      // update the DICOM dataset
+      return; 
+    }
+      
+    currentDataset_.reset(loader_->DownloadDicom(frame));
+    currentFrame_ = frame;
+
+    if (currentDataset_.get() == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+    }
+  }
+
+
+  void SeriesFrameRendererFactory::GetCurrentPixelSpacing(double& spacingX,
+                                                          double& spacingY) const
+  {
+    if (currentDataset_.get() == NULL)
+    {
+      // There was no previous call "ReadCurrentFrameDataset()"
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+    }
+    
+    GeometryToolbox::GetPixelSpacing(spacingX, spacingY, *currentDataset_);
+  }
+
+
+  double SeriesFrameRendererFactory::GetCurrentSliceThickness() const
+  {
+    if (currentDataset_.get() == NULL)
+    {
+      // There was no previous call "ReadCurrentFrameDataset()"
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+    }
+    
+    try
+    {
+      OrthancPlugins::DicomDatasetReader reader(*currentDataset_);
+
+      double thickness;
+      if (reader.GetDoubleValue(thickness, OrthancPlugins::DICOM_TAG_SLICE_THICKNESS))
+      {
+        return thickness;
+      }
+    }
+    catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
+    {
+    }
+
+    // Some arbitrary large slice thickness
+    return std::numeric_limits<double>::infinity();
+  }
+
+
+  SeriesFrameRendererFactory::SeriesFrameRendererFactory(ISeriesLoader* loader,   // Takes ownership
+                                                         bool fast) :
+    loader_(loader),
+    currentFrame_(0),
+    fast_(fast)
+  {
+    if (loader == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+    }
+  }
+
+
+  bool SeriesFrameRendererFactory::GetExtent(double& x1,
+                                             double& y1,
+                                             double& x2,
+                                             double& y2,
+                                             const SliceGeometry& viewportSlice)
+  {
+    if (currentDataset_.get() == NULL)
+    {
+      // There has been no previous call to
+      // "CreateLayerRenderer". Read some arbitrary DICOM frame, the
+      // one at the middle of the series.
+      unsigned int depth = loader_->GetGeometry().GetSliceCount();
+      ReadCurrentFrameDataset(depth / 2);
+    }
+
+    double spacingX, spacingY;
+    GetCurrentPixelSpacing(spacingX, spacingY);
+
+    return FrameRenderer::ComputeFrameExtent(x1, y1, x2, y2, 
+                                             viewportSlice, 
+                                             loader_->GetGeometry().GetSlice(0), 
+                                             loader_->GetWidth(), 
+                                             loader_->GetHeight(),
+                                             spacingX, spacingY);
+  }
+
+
+  ILayerRenderer* SeriesFrameRendererFactory::CreateLayerRenderer(const SliceGeometry& viewportSlice)
+  {
+    size_t closest;
+    double distance;
+
+    bool isOpposite;
+    if (!GeometryToolbox::IsParallelOrOpposite(isOpposite, loader_->GetGeometry().GetNormal(), viewportSlice.GetNormal()) ||
+        !loader_->GetGeometry().ComputeClosestSlice(closest, distance, viewportSlice.GetOrigin()))
+    {
+      // Unable to compute the slice in the series that is the
+      // closest to the slice displayed by the viewport
+      return NULL;
+    }
+
+    ReadCurrentFrameDataset(closest);
+    assert(currentDataset_.get() != NULL);
+        
+    double spacingX, spacingY;
+    GetCurrentPixelSpacing(spacingX, spacingY);
+
+    if (distance <= GetCurrentSliceThickness() / 2.0)
+    {
+      SliceGeometry frameSlice(*currentDataset_);
+      return FrameRenderer::CreateRenderer(loader_->DownloadFrame(closest), 
+                                           frameSlice,
+                                           *currentDataset_, 
+                                           spacingX, spacingY,
+                                           true);
+    }
+    else
+    {
+      // The closest slice of the series is too far away from the
+      // slice displayed by the viewport
+      return NULL;
+    }
+  }
+
+
+  ISliceableVolume& SeriesFrameRendererFactory::GetSourceVolume() const
+  {
+    throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Layers/SeriesFrameRendererFactory.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,65 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "ILayerRendererFactory.h"
+
+#include "../Toolbox/ISeriesLoader.h"
+
+namespace Deprecated
+{
+  class SeriesFrameRendererFactory : public ILayerRendererFactory
+  {
+  private:
+    std::auto_ptr<ISeriesLoader>  loader_;
+    size_t                        currentFrame_;
+    bool                          fast_;
+
+    std::auto_ptr<OrthancPlugins::IDicomDataset>  currentDataset_;
+
+    void ReadCurrentFrameDataset(size_t frame);
+
+    void GetCurrentPixelSpacing(double& spacingX,
+                                double& spacingY) const;
+
+    double GetCurrentSliceThickness() const;
+
+  public:
+    SeriesFrameRendererFactory(ISeriesLoader* loader,   // Takes ownership
+                               bool fast);
+
+    virtual bool GetExtent(double& x1,
+                           double& y1,
+                           double& x2,
+                           double& y2,
+                           const SliceGeometry& viewportSlice);
+
+    virtual ILayerRenderer* CreateLayerRenderer(const SliceGeometry& viewportSlice);
+
+    virtual bool HasSourceVolume() const
+    {
+      return false;
+    }
+
+    virtual ISliceableVolume& GetSourceVolume() const;
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Layers/SingleFrameRendererFactory.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,88 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "SingleFrameRendererFactory.h"
+
+#include "FrameRenderer.h"
+#include "../Toolbox/MessagingToolbox.h"
+#include "../Toolbox/DicomFrameConverter.h"
+
+#include <OrthancException.h>
+#include <Plugins/Samples/Common/FullOrthancDataset.h>
+#include <Plugins/Samples/Common/DicomDatasetReader.h>
+
+namespace Deprecated
+{
+  SingleFrameRendererFactory::SingleFrameRendererFactory(OrthancPlugins::IOrthancConnection& orthanc,
+                                                         const std::string& instanceId,
+                                                         unsigned int frame) :
+    orthanc_(orthanc),
+    instance_(instanceId),
+    frame_(frame)
+  {
+    dicom_.reset(new OrthancPlugins::FullOrthancDataset(orthanc, "/instances/" + instanceId + "/tags"));
+
+    DicomFrameConverter converter;
+    converter.ReadParameters(*dicom_);
+    format_ = converter.GetExpectedPixelFormat();
+  }
+
+
+  bool SingleFrameRendererFactory::GetExtent(double& x1,
+                                             double& y1,
+                                             double& x2,
+                                             double& y2,
+                                             const SliceGeometry& viewportSlice)
+  {
+    // Assume that PixelSpacingX == PixelSpacingY == 1
+
+    OrthancPlugins::DicomDatasetReader reader(*dicom_);
+
+    unsigned int width, height;
+
+    if (!reader.GetUnsignedIntegerValue(width, OrthancPlugins::DICOM_TAG_COLUMNS) ||
+        !reader.GetUnsignedIntegerValue(height, OrthancPlugins::DICOM_TAG_ROWS))
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+    }
+
+    x1 = 0;
+    y1 = 0;
+    x2 = static_cast<double>(width);
+    y2 = static_cast<double>(height);
+
+    return true;
+  }
+
+
+  ILayerRenderer* SingleFrameRendererFactory::CreateLayerRenderer(const SliceGeometry& viewportSlice)
+  {
+    SliceGeometry frameSlice(*dicom_);
+    return FrameRenderer::CreateRenderer(MessagingToolbox::DecodeFrame(orthanc_, instance_, frame_, format_), 
+                                         frameSlice, *dicom_, 1, 1, true);
+  }
+
+
+  ISliceableVolume& SingleFrameRendererFactory::GetSourceVolume() const
+  {
+    throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Layers/SingleFrameRendererFactory.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,69 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "ILayerRendererFactory.h"
+#include <Plugins/Samples/Common/IOrthancConnection.h>
+
+namespace Deprecated
+{
+  class SingleFrameRendererFactory : public ILayerRendererFactory
+  {
+  private:
+    OrthancPlugins::IOrthancConnection&           orthanc_;
+    std::auto_ptr<OrthancPlugins::IDicomDataset>  dicom_;
+
+    std::string           instance_;
+    unsigned int          frame_;
+    Orthanc::PixelFormat  format_;
+
+  public:
+    SingleFrameRendererFactory(OrthancPlugins::IOrthancConnection& orthanc,
+                               const std::string& instanceId,
+                               unsigned int frame);
+
+    const OrthancPlugins::IDicomDataset& GetDataset() const
+    {
+      return *dicom_;
+    }
+
+    SliceGeometry GetSliceGeometry()
+    {
+      return SliceGeometry(*dicom_);
+    }
+
+    virtual bool GetExtent(double& x1,
+                           double& y1,
+                           double& x2,
+                           double& y2,
+                           const SliceGeometry& viewportSlice);
+
+    virtual ILayerRenderer* CreateLayerRenderer(const SliceGeometry& viewportSlice);
+
+    virtual bool HasSourceVolume() const
+    {
+      return false;
+    }
+
+    virtual ISliceableVolume& GetSourceVolume() const;
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Layers/SliceOutlineRenderer.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,54 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "SliceOutlineRenderer.h"
+
+namespace Deprecated
+{
+  bool SliceOutlineRenderer::RenderLayer(OrthancStone::CairoContext& context,
+                                         const ViewportGeometry& view)
+  {
+    if (style_.visible_)
+    {
+      cairo_t *cr = context.GetObject();
+      cairo_save(cr);
+
+      context.SetSourceColor(style_.drawColor_);
+
+      double x1 = -0.5 * pixelSpacingX_;
+      double y1 = -0.5 * pixelSpacingY_;
+        
+      cairo_set_line_width(cr, 1.0 / view.GetZoom());
+      cairo_rectangle(cr, x1, y1,
+                      static_cast<double>(width_) * pixelSpacingX_,
+                      static_cast<double>(height_) * pixelSpacingY_);
+
+      double handleSize = 10.0f / view.GetZoom();
+      cairo_move_to(cr, x1 + handleSize, y1);
+      cairo_line_to(cr, x1, y1 + handleSize);
+
+      cairo_stroke(cr);
+      cairo_restore(cr);
+    }
+
+    return true;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Layers/SliceOutlineRenderer.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,67 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "ILayerRenderer.h"
+#include "../Toolbox/Slice.h"
+
+namespace Deprecated
+{
+  class SliceOutlineRenderer : public ILayerRenderer
+  {
+  private:
+    OrthancStone::CoordinateSystem3D  geometry_;
+    double              pixelSpacingX_;
+    double              pixelSpacingY_;
+    unsigned int        width_;
+    unsigned int        height_;
+    RenderStyle         style_;
+
+  public:
+    SliceOutlineRenderer(const Slice& slice) :
+      geometry_(slice.GetGeometry()),
+      pixelSpacingX_(slice.GetPixelSpacingX()),
+      pixelSpacingY_(slice.GetPixelSpacingY()),
+      width_(slice.GetWidth()),
+      height_(slice.GetHeight())
+    {
+    }
+
+    virtual bool RenderLayer(OrthancStone::CairoContext& context,
+                             const ViewportGeometry& view);
+
+    virtual void SetLayerStyle(const RenderStyle& style)
+    {
+      style_ = style;
+    }
+
+    virtual const OrthancStone::CoordinateSystem3D& GetLayerSlice()
+    {
+      return geometry_;
+    }
+
+    virtual bool IsFullQuality()
+    {
+      return true;
+    }
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/SmartLoader.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,292 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "SmartLoader.h"
+
+#include "../Messages/MessageForwarder.h"
+#include "../StoneException.h"
+#include "Core/Images/Image.h"
+#include "Core/Logging.h"
+#include "Layers/DicomSeriesVolumeSlicer.h"
+#include "Layers/FrameRenderer.h"
+#include "Widgets/SliceViewerWidget.h"
+
+namespace Deprecated
+{
+  enum CachedSliceStatus
+  {
+    CachedSliceStatus_ScheduledToLoad,
+    CachedSliceStatus_GeometryLoaded,
+    CachedSliceStatus_ImageLoaded
+  };
+
+  class SmartLoader::CachedSlice : public IVolumeSlicer
+  {
+  public:
+    class RendererFactory : public LayerReadyMessage::IRendererFactory
+    {
+    private:
+      const CachedSlice&  that_;
+
+    public:
+      RendererFactory(const CachedSlice& that) :
+        that_(that)
+      {
+      }
+
+      virtual ILayerRenderer* CreateRenderer() const
+      {
+        bool isFull = (that_.effectiveQuality_ == SliceImageQuality_FullPng ||
+                       that_.effectiveQuality_ == SliceImageQuality_FullPam);
+
+        return FrameRenderer::CreateRenderer(*that_.image_, *that_.slice_, isFull);
+      }
+    };
+    
+    unsigned int                    sliceIndex_;
+    std::auto_ptr<Slice>            slice_;
+    boost::shared_ptr<Orthanc::ImageAccessor>   image_;
+    SliceImageQuality               effectiveQuality_;
+    CachedSliceStatus               status_;
+
+  public:
+    CachedSlice(OrthancStone::MessageBroker& broker) :
+    IVolumeSlicer(broker)
+    {
+    }
+
+    virtual ~CachedSlice()
+    {
+    }
+
+    virtual bool GetExtent(std::vector<OrthancStone::Vector>& points,
+                           const OrthancStone::CoordinateSystem3D& viewportSlice)
+    {
+      // TODO: viewportSlice is not used !!!!
+      slice_->GetExtent(points);
+      return true;
+    }
+
+    virtual void ScheduleLayerCreation(const OrthancStone::CoordinateSystem3D& viewportSlice)
+    {
+      // TODO: viewportSlice is not used !!!!
+
+      // it has already been loaded -> trigger the "layer ready" message immediately otherwise, do nothing now.  The LayerReady will be triggered
+      // once the VolumeSlicer is ready
+      if (status_ == CachedSliceStatus_ImageLoaded)
+      {
+        LOG(WARNING) << "ScheduleLayerCreation for CachedSlice (image is loaded): " << slice_->GetOrthancInstanceId();
+
+        RendererFactory factory(*this);   
+        BroadcastMessage(IVolumeSlicer::LayerReadyMessage(*this, factory, slice_->GetGeometry()));
+      }
+      else
+      {
+        LOG(WARNING) << "ScheduleLayerCreation for CachedSlice (image is not loaded yet): " << slice_->GetOrthancInstanceId();
+      }
+    }
+
+    CachedSlice* Clone() const
+    {
+      CachedSlice* output = new CachedSlice(GetBroker());
+      output->sliceIndex_ = sliceIndex_;
+      output->slice_.reset(slice_->Clone());
+      output->image_ = image_;
+      output->effectiveQuality_ = effectiveQuality_;
+      output->status_ = status_;
+
+      return output;
+    }
+
+  };
+
+
+  SmartLoader::SmartLoader(OrthancStone::MessageBroker& broker,  
+                           OrthancApiClient& orthancApiClient) :
+    IObservable(broker),
+    IObserver(broker),
+    imageQuality_(SliceImageQuality_FullPam),
+    orthancApiClient_(orthancApiClient)
+  {
+  }
+
+  void SmartLoader::SetFrameInWidget(SliceViewerWidget& sliceViewer, 
+                                     size_t layerIndex, 
+                                     const std::string& instanceId, 
+                                     unsigned int frame)
+  {
+    // TODO: check if this frame has already been loaded or is already being loaded.
+    // - if already loaded: create a "clone" that will emit the GeometryReady/ImageReady messages "immediately"
+    //   (it can not be immediate because Observers needs to register first and this is done after this method returns)
+    // - if currently loading, we need to return an object that will observe the existing VolumeSlicer and forward
+    //   the messages to its observables
+    // in both cases, we must be carefull about objects lifecycle !!!
+
+    std::auto_ptr<IVolumeSlicer> layerSource;
+    std::string sliceKeyId = instanceId + ":" + boost::lexical_cast<std::string>(frame);
+    SmartLoader::CachedSlice* cachedSlice = NULL;
+
+    if (cachedSlices_.find(sliceKeyId) != cachedSlices_.end()) // && cachedSlices_[sliceKeyId]->status_ == CachedSliceStatus_Loaded)
+    {
+      layerSource.reset(cachedSlices_[sliceKeyId]->Clone());
+      cachedSlice = dynamic_cast<SmartLoader::CachedSlice*>(layerSource.get());
+    }
+    else
+    {
+      layerSource.reset(new DicomSeriesVolumeSlicer(IObserver::GetBroker(), orthancApiClient_));
+      dynamic_cast<DicomSeriesVolumeSlicer*>(layerSource.get())->SetImageQuality(imageQuality_);
+      layerSource->RegisterObserverCallback(new OrthancStone::Callable<SmartLoader, IVolumeSlicer::GeometryReadyMessage>(*this, &SmartLoader::OnLayerGeometryReady));
+      layerSource->RegisterObserverCallback(new OrthancStone::Callable<SmartLoader, DicomSeriesVolumeSlicer::FrameReadyMessage>(*this, &SmartLoader::OnFrameReady));
+      layerSource->RegisterObserverCallback(new OrthancStone::Callable<SmartLoader, IVolumeSlicer::LayerReadyMessage>(*this, &SmartLoader::OnLayerReady));
+      dynamic_cast<DicomSeriesVolumeSlicer*>(layerSource.get())->LoadFrame(instanceId, frame);
+    }
+
+    // make sure that the widget registers the events before we trigger them
+    if (sliceViewer.GetLayerCount() == layerIndex)
+    {
+      sliceViewer.AddLayer(layerSource.release());
+    }
+    else if (sliceViewer.GetLayerCount() > layerIndex)
+    {
+      sliceViewer.ReplaceLayer(layerIndex, layerSource.release());
+    }
+    else
+    {
+      throw OrthancStone::StoneException(OrthancStone::ErrorCode_CanOnlyAddOneLayerAtATime);
+    }
+
+    if (cachedSlice != NULL)
+    {
+      BroadcastMessage(IVolumeSlicer::GeometryReadyMessage(*cachedSlice));
+    }
+
+  }
+
+  void SmartLoader::PreloadSlice(const std::string instanceId, 
+                                 unsigned int frame)
+  {
+    // TODO: reactivate -> need to be able to ScheduleLayerLoading in IVolumeSlicer without calling ScheduleLayerCreation
+    return;
+    // TODO: check if it is already in the cache
+
+
+
+    // create the slice in the cache with "empty" data
+    boost::shared_ptr<CachedSlice> cachedSlice(new CachedSlice(IObserver::GetBroker()));
+    cachedSlice->slice_.reset(new Slice(instanceId, frame));
+    cachedSlice->status_ = CachedSliceStatus_ScheduledToLoad;
+    std::string sliceKeyId = instanceId + ":" + boost::lexical_cast<std::string>(frame);
+
+    LOG(WARNING) << "Will preload: " << sliceKeyId;
+
+    cachedSlices_[sliceKeyId] = boost::shared_ptr<CachedSlice>(cachedSlice);
+
+    std::auto_ptr<IVolumeSlicer> layerSource(new DicomSeriesVolumeSlicer(IObserver::GetBroker(), orthancApiClient_));
+
+    dynamic_cast<DicomSeriesVolumeSlicer*>(layerSource.get())->SetImageQuality(imageQuality_);
+    layerSource->RegisterObserverCallback(new OrthancStone::Callable<SmartLoader, IVolumeSlicer::GeometryReadyMessage>(*this, &SmartLoader::OnLayerGeometryReady));
+    layerSource->RegisterObserverCallback(new OrthancStone::Callable<SmartLoader, DicomSeriesVolumeSlicer::FrameReadyMessage>(*this, &SmartLoader::OnFrameReady));
+    layerSource->RegisterObserverCallback(new OrthancStone::Callable<SmartLoader, IVolumeSlicer::LayerReadyMessage>(*this, &SmartLoader::OnLayerReady));
+    dynamic_cast<DicomSeriesVolumeSlicer*>(layerSource.get())->LoadFrame(instanceId, frame);
+
+    // keep a ref to the VolumeSlicer until the slice is fully loaded and saved to cache
+    preloadingInstances_[sliceKeyId] = boost::shared_ptr<IVolumeSlicer>(layerSource.release());
+  }
+
+
+//  void PreloadStudy(const std::string studyId)
+//  {
+//    /* TODO */
+//  }
+
+//  void PreloadSeries(const std::string seriesId)
+//  {
+//    /* TODO */
+//  }
+
+
+  void SmartLoader::OnLayerGeometryReady(const IVolumeSlicer::GeometryReadyMessage& message)
+  {
+    const DicomSeriesVolumeSlicer& source =
+      dynamic_cast<const DicomSeriesVolumeSlicer&>(message.GetOrigin());
+
+    // save/replace the slice in cache
+    const Slice& slice = source.GetSlice(0); // TODO handle GetSliceCount()
+    std::string sliceKeyId = (slice.GetOrthancInstanceId() + ":" + 
+                              boost::lexical_cast<std::string>(slice.GetFrame()));
+
+    LOG(WARNING) << "Geometry ready: " << sliceKeyId;
+
+    boost::shared_ptr<CachedSlice> cachedSlice(new CachedSlice(IObserver::GetBroker()));
+    cachedSlice->slice_.reset(slice.Clone());
+    cachedSlice->effectiveQuality_ = source.GetImageQuality();
+    cachedSlice->status_ = CachedSliceStatus_GeometryLoaded;
+
+    cachedSlices_[sliceKeyId] = boost::shared_ptr<CachedSlice>(cachedSlice);
+
+    // re-emit original Layer message to observers
+    BroadcastMessage(message);
+  }
+
+
+  void SmartLoader::OnFrameReady(const DicomSeriesVolumeSlicer::FrameReadyMessage& message)
+  {
+    // save/replace the slice in cache
+    const Slice& slice = message.GetSlice();
+    std::string sliceKeyId = (slice.GetOrthancInstanceId() + ":" + 
+                              boost::lexical_cast<std::string>(slice.GetFrame()));
+
+    LOG(WARNING) << "Image ready: " << sliceKeyId;
+
+    boost::shared_ptr<CachedSlice> cachedSlice(new CachedSlice(IObserver::GetBroker()));
+    cachedSlice->image_.reset(Orthanc::Image::Clone(message.GetFrame()));
+    cachedSlice->effectiveQuality_ = message.GetImageQuality();
+    cachedSlice->slice_.reset(message.GetSlice().Clone());
+    cachedSlice->status_ = CachedSliceStatus_ImageLoaded;
+
+    cachedSlices_[sliceKeyId] = cachedSlice;
+
+    // re-emit original Layer message to observers
+    BroadcastMessage(message);
+  }
+
+
+  void SmartLoader::OnLayerReady(const IVolumeSlicer::LayerReadyMessage& message)
+  {
+    const DicomSeriesVolumeSlicer& source =
+      dynamic_cast<const DicomSeriesVolumeSlicer&>(message.GetOrigin());
+    
+    const Slice& slice = source.GetSlice(0); // TODO handle GetSliceCount() ?
+    std::string sliceKeyId = (slice.GetOrthancInstanceId() + ":" + 
+                              boost::lexical_cast<std::string>(slice.GetFrame()));
+
+    LOG(WARNING) << "Layer ready: " << sliceKeyId;
+
+    // remove the slice from the preloading slices now that it has been fully loaded and it is referenced in the cache
+    if (preloadingInstances_.find(sliceKeyId) != preloadingInstances_.end())
+    {
+      preloadingInstances_.erase(sliceKeyId);
+    }
+
+    // re-emit original Layer message to observers
+    BroadcastMessage(message);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/SmartLoader.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,67 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+#include <map>
+
+#include "Layers/DicomSeriesVolumeSlicer.h"
+#include "../Messages/IObservable.h"
+#include "Toolbox/OrthancApiClient.h"
+
+namespace Deprecated
+{
+  class SliceViewerWidget;
+
+  class SmartLoader : public OrthancStone::IObservable, public OrthancStone::IObserver
+  {
+    class CachedSlice;
+
+  protected:
+    typedef std::map<std::string, boost::shared_ptr<SmartLoader::CachedSlice> > CachedSlices;
+    CachedSlices cachedSlices_;
+
+    typedef std::map<std::string, boost::shared_ptr<IVolumeSlicer> > PreloadingInstances;
+    PreloadingInstances preloadingInstances_;
+
+    SliceImageQuality     imageQuality_;
+    OrthancApiClient&     orthancApiClient_;
+
+  public:
+    SmartLoader(OrthancStone::MessageBroker& broker, OrthancApiClient& orthancApiClient);  // TODO: add maxPreloadStorageSizeInBytes
+
+//    void PreloadStudy(const std::string studyId);
+//    void PreloadSeries(const std::string seriesId);
+    void PreloadSlice(const std::string instanceId, unsigned int frame);
+
+    void SetImageQuality(SliceImageQuality imageQuality) { imageQuality_ = imageQuality; }
+
+    void SetFrameInWidget(SliceViewerWidget& sliceViewer, size_t layerIndex, const std::string& instanceId, unsigned int frame);
+
+    void GetFirstInstanceIdForSeries(std::string& output, const std::string& seriesId);
+
+  private:
+    void OnLayerGeometryReady(const IVolumeSlicer::GeometryReadyMessage& message);
+    void OnFrameReady(const DicomSeriesVolumeSlicer::FrameReadyMessage& message);
+    void OnLayerReady(const IVolumeSlicer::LayerReadyMessage& message);
+
+  };
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Toolbox/BaseWebService.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,145 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2018 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 "BaseWebService.h"
+
+#include "../../Messages/IObservable.h"
+#include "../../../Platforms/Generic/IOracleCommand.h"
+
+#include <Core/OrthancException.h>
+
+#include <boost/shared_ptr.hpp>
+
+namespace Deprecated
+{
+
+
+  class BaseWebService::BaseWebServicePayload : public Orthanc::IDynamicObject
+  {
+  private:
+    std::auto_ptr< OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> >   userSuccessHandler_;
+    std::auto_ptr< OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage> >     userFailureHandler_;
+    std::auto_ptr< Orthanc::IDynamicObject>                                   userPayload_;
+
+  public:
+    BaseWebServicePayload(OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* userSuccessHandler,
+                          OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* userFailureHandler,
+                          Orthanc::IDynamicObject* userPayload) :
+      userSuccessHandler_(userSuccessHandler),
+      userFailureHandler_(userFailureHandler),
+      userPayload_(userPayload)
+    {
+    }
+
+    void HandleSuccess(const IWebService::HttpRequestSuccessMessage& message) const
+    {
+      if (userSuccessHandler_.get() != NULL)
+      {
+        // recreate a success message with the user payload
+        IWebService::HttpRequestSuccessMessage successMessage(message.GetUri(),
+                                                              message.GetAnswer(),
+                                                              message.GetAnswerSize(),
+                                                              message.GetAnswerHttpHeaders(),
+                                                              userPayload_.get());
+        userSuccessHandler_->Apply(successMessage);
+      }
+      else
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      }
+    }
+
+    void HandleFailure(const IWebService::HttpRequestErrorMessage& message) const
+    {
+      if (userFailureHandler_.get() != NULL)
+      {
+        // recreate a failure message with the user payload
+        IWebService::HttpRequestErrorMessage failureMessage(message.GetUri(),
+                                                            userPayload_.get());
+
+        userFailureHandler_->Apply(failureMessage);
+      }
+    }
+
+  };
+
+
+  void BaseWebService::GetAsync(const std::string& uri,
+                                const HttpHeaders& headers,
+                                Orthanc::IDynamicObject* payload  /* takes ownership */,
+                                OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,
+                                OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,
+                                unsigned int timeoutInSeconds)
+  {
+    if (cache_.find(uri) == cache_.end())
+    {
+      GetAsyncInternal(uri, headers,
+                       new BaseWebService::BaseWebServicePayload(successCallback, failureCallback, payload), // ownership is transfered
+                       new OrthancStone::Callable<BaseWebService, IWebService::HttpRequestSuccessMessage>
+                       (*this, &BaseWebService::CacheAndNotifyHttpSuccess),
+                       new OrthancStone::Callable<BaseWebService, IWebService::HttpRequestErrorMessage>
+                       (*this, &BaseWebService::NotifyHttpError),
+                       timeoutInSeconds);
+    }
+    else
+    {
+      // create a command and "post" it to the Oracle so it is executed and commited "later"
+      NotifyHttpSuccessLater(cache_[uri], payload, successCallback);
+    }
+
+  }
+
+
+
+  void BaseWebService::NotifyHttpSuccess(const IWebService::HttpRequestSuccessMessage& message)
+  {
+    if (message.HasPayload())
+    {
+      dynamic_cast<const BaseWebServicePayload&>(message.GetPayload()).HandleSuccess(message);
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+    }
+  }
+
+  void BaseWebService::CacheAndNotifyHttpSuccess(const IWebService::HttpRequestSuccessMessage& message)
+  {
+    cache_[message.GetUri()] = boost::shared_ptr<CachedHttpRequestSuccessMessage>(new CachedHttpRequestSuccessMessage(message));
+    NotifyHttpSuccess(message);
+  }
+
+  void BaseWebService::NotifyHttpError(const IWebService::HttpRequestErrorMessage& message)
+  {
+    if (message.HasPayload())
+    {
+      dynamic_cast<const BaseWebServicePayload&>(message.GetPayload()).HandleFailure(message);
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+    }
+  }
+
+
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Toolbox/BaseWebService.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,131 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2018 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 "IWebService.h"
+
+#include <string>
+#include <map>
+
+namespace Deprecated
+{
+  // This is an intermediate of IWebService that implements some caching on
+  // the HTTP GET requests
+  class BaseWebService : public IWebService, public OrthancStone::IObserver
+  {
+  public:
+    class CachedHttpRequestSuccessMessage
+    {
+    protected:
+      std::string                    uri_;
+      void*                          answer_;
+      size_t                         answerSize_;
+      IWebService::HttpHeaders       answerHeaders_;
+
+    public:
+      CachedHttpRequestSuccessMessage(const IWebService::HttpRequestSuccessMessage& message) :
+        uri_(message.GetUri()),
+        answerSize_(message.GetAnswerSize()),
+        answerHeaders_(message.GetAnswerHttpHeaders())
+      {
+        answer_ =  malloc(answerSize_);
+        memcpy(answer_, message.GetAnswer(), answerSize_);
+      }
+
+      ~CachedHttpRequestSuccessMessage()
+      {
+        free(answer_);
+      }
+
+      const std::string& GetUri() const
+      {
+        return uri_;
+      }
+
+      const void* GetAnswer() const
+      {
+        return answer_;
+      }
+
+      size_t GetAnswerSize() const
+      {
+        return answerSize_;
+      }
+
+      const IWebService::HttpHeaders&  GetAnswerHttpHeaders() const
+      {
+        return answerHeaders_;
+      }
+
+    };
+  protected:
+    class BaseWebServicePayload;
+
+    bool          cacheEnabled_;
+    std::map<std::string, boost::shared_ptr<CachedHttpRequestSuccessMessage> > cache_;  // TODO: this is currently an infinite cache !
+
+  public:
+
+    BaseWebService(OrthancStone::MessageBroker& broker) :
+      IWebService(broker),
+      IObserver(broker),
+      cacheEnabled_(true)
+    {
+    }
+
+    virtual ~BaseWebService()
+    {
+    }
+
+    virtual void EnableCache(bool enable)
+    {
+      cacheEnabled_ = enable;
+    }
+
+    virtual void GetAsync(const std::string& uri,
+                          const HttpHeaders& headers,
+                          Orthanc::IDynamicObject* payload  /* takes ownership */,
+                          OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,
+                          OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
+                          unsigned int timeoutInSeconds = 60);
+
+  protected:
+    virtual void GetAsyncInternal(const std::string& uri,
+                          const HttpHeaders& headers,
+                          Orthanc::IDynamicObject* payload  /* takes ownership */,
+                          OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,
+                          OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
+                          unsigned int timeoutInSeconds = 60) = 0;
+
+    virtual void NotifyHttpSuccessLater(boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedHttpMessage,
+                                        Orthanc::IDynamicObject* payload, // takes ownership
+                                        OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback) = 0;
+
+  private:
+    void NotifyHttpSuccess(const IWebService::HttpRequestSuccessMessage& message);
+
+    void NotifyHttpError(const IWebService::HttpRequestErrorMessage& message);
+
+    void CacheAndNotifyHttpSuccess(const IWebService::HttpRequestSuccessMessage& message);
+
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Toolbox/DicomFrameConverter.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,282 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "DicomFrameConverter.h"
+
+#include "../../Toolbox/LinearAlgebra.h"
+
+#include <Core/Images/Image.h>
+#include <Core/Images/ImageProcessing.h>
+#include <Core/OrthancException.h>
+#include <Core/Toolbox.h>
+
+namespace Deprecated
+{
+  static const Orthanc::DicomTag IMAGE_TAGS[] =
+  {
+    Orthanc::DICOM_TAG_BITS_STORED,
+    Orthanc::DICOM_TAG_DOSE_GRID_SCALING,
+    Orthanc::DICOM_TAG_PHOTOMETRIC_INTERPRETATION,
+    Orthanc::DICOM_TAG_PIXEL_REPRESENTATION,
+    Orthanc::DICOM_TAG_RESCALE_INTERCEPT,
+    Orthanc::DICOM_TAG_RESCALE_SLOPE,
+    Orthanc::DICOM_TAG_WINDOW_CENTER,
+    Orthanc::DICOM_TAG_WINDOW_WIDTH
+  };
+
+  
+  void DicomFrameConverter::SetDefaultParameters()
+  {
+    isSigned_ = true;
+    isColor_ = false;
+    hasRescale_ = false;
+    rescaleIntercept_ = 0;
+    rescaleSlope_ = 1;
+    hasDefaultWindow_ = false;
+    defaultWindowCenter_ = 128;
+    defaultWindowWidth_ = 256;
+    expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale16;
+  }
+
+
+  void DicomFrameConverter::ReadParameters(const Orthanc::DicomMap& dicom)
+  {
+    SetDefaultParameters();
+
+    OrthancStone::Vector c, w;
+    if (OrthancStone::LinearAlgebra::ParseVector(c, dicom, Orthanc::DICOM_TAG_WINDOW_CENTER) &&
+        OrthancStone::LinearAlgebra::ParseVector(w, dicom, Orthanc::DICOM_TAG_WINDOW_WIDTH) &&
+        c.size() > 0 && 
+        w.size() > 0)
+    {
+      hasDefaultWindow_ = true;
+      defaultWindowCenter_ = static_cast<float>(c[0]);
+      defaultWindowWidth_ = static_cast<float>(w[0]);
+    }
+
+    int32_t tmp;
+    if (!dicom.ParseInteger32(tmp, Orthanc::DICOM_TAG_PIXEL_REPRESENTATION))
+    {
+      // Type 1 tag, must be present
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+    }
+
+    isSigned_ = (tmp == 1);
+
+    double doseGridScaling;
+    bool isRTDose = false;
+    
+    if (dicom.ParseDouble(rescaleIntercept_, Orthanc::DICOM_TAG_RESCALE_INTERCEPT) &&
+        dicom.ParseDouble(rescaleSlope_, Orthanc::DICOM_TAG_RESCALE_SLOPE))
+    {
+      hasRescale_ = true;
+    }
+    else if (dicom.ParseDouble(doseGridScaling, Orthanc::DICOM_TAG_DOSE_GRID_SCALING))
+    {
+      // This is for RT-DOSE
+      hasRescale_ = true;
+      isRTDose = true;
+      rescaleIntercept_ = 0;
+      rescaleSlope_ = doseGridScaling;
+
+      if (!dicom.ParseInteger32(tmp, Orthanc::DICOM_TAG_BITS_STORED))
+      {
+        // Type 1 tag, must be present
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+      }
+
+      switch (tmp)
+      {
+        case 16:
+          expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale16;
+          break;
+
+        case 32:
+          expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale32;
+          break;
+
+        default:
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+      }
+    }
+
+    std::string photometric;
+    if (dicom.CopyToString(photometric, Orthanc::DICOM_TAG_PHOTOMETRIC_INTERPRETATION, false))
+    {
+      photometric = Orthanc::Toolbox::StripSpaces(photometric);
+    }
+    else
+    {
+      // Type 1 tag, must be present
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+    }
+
+    photometric_ = Orthanc::StringToPhotometricInterpretation(photometric.c_str());
+    
+    isColor_ = (photometric != "MONOCHROME1" &&
+                photometric != "MONOCHROME2");
+
+    // TODO Add more checks, e.g. on the number of bytes per value
+    // (cf. DicomImageInformation.h in Orthanc)
+
+    if (!isRTDose)
+    {
+      if (isColor_)
+      {
+        expectedPixelFormat_ = Orthanc::PixelFormat_RGB24;
+      }
+      else if (isSigned_)
+      {
+        expectedPixelFormat_ = Orthanc::PixelFormat_SignedGrayscale16;
+      }
+      else
+      {
+        expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale16;
+      }
+    }
+  }
+
+  
+  void DicomFrameConverter::ReadParameters(const OrthancPlugins::IDicomDataset& dicom)
+  {
+    Orthanc::DicomMap converted;
+
+    for (size_t i = 0; i < sizeof(IMAGE_TAGS) / sizeof(Orthanc::DicomTag); i++)
+    {
+      OrthancPlugins::DicomTag tag(IMAGE_TAGS[i].GetGroup(), IMAGE_TAGS[i].GetElement());
+    
+      std::string value;
+      if (dicom.GetStringValue(value, tag))
+      {
+        converted.SetValue(IMAGE_TAGS[i], value, false);
+      }
+    }
+
+    ReadParameters(converted);
+  }
+    
+
+  void DicomFrameConverter::ConvertFrameInplace(std::auto_ptr<Orthanc::ImageAccessor>& source) const
+  {
+    assert(sizeof(float) == 4);
+
+    if (source.get() == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+
+    if (source->GetFormat() == GetExpectedPixelFormat() &&
+        source->GetFormat() == Orthanc::PixelFormat_RGB24)
+    {
+      // No conversion has to be done, check out (*)
+      return;
+    }
+    else
+    {
+      source.reset(ConvertFrame(*source));
+    }
+  }
+
+
+  Orthanc::ImageAccessor* DicomFrameConverter::ConvertFrame(const Orthanc::ImageAccessor& source) const
+  {
+    assert(sizeof(float) == 4);
+
+    Orthanc::PixelFormat sourceFormat = source.GetFormat();
+
+    if (sourceFormat != GetExpectedPixelFormat())
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
+    }
+
+    if (sourceFormat == Orthanc::PixelFormat_RGB24)
+    {
+      // This is the case of a color image. No conversion has to be done (*)
+      std::auto_ptr<Orthanc::Image> converted(new Orthanc::Image(Orthanc::PixelFormat_RGB24, 
+                                                                 source.GetWidth(), 
+                                                                 source.GetHeight(),
+                                                                 false));
+      Orthanc::ImageProcessing::Copy(*converted, source);
+      return converted.release();
+    }
+    else
+    {
+      assert(sourceFormat == Orthanc::PixelFormat_Grayscale16 ||
+             sourceFormat == Orthanc::PixelFormat_Grayscale32 ||
+             sourceFormat == Orthanc::PixelFormat_SignedGrayscale16);
+
+      // This is the case of a grayscale frame. Convert it to Float32.
+      std::auto_ptr<Orthanc::Image> converted(new Orthanc::Image(Orthanc::PixelFormat_Float32, 
+                                                                 source.GetWidth(), 
+                                                                 source.GetHeight(),
+                                                                 false));
+      Orthanc::ImageProcessing::Convert(*converted, source);
+
+      // Correct rescale slope/intercept if need be
+      ApplyRescale(*converted, sourceFormat != Orthanc::PixelFormat_Grayscale32);
+      
+      return converted.release();
+    }
+  }
+
+
+  void DicomFrameConverter::ApplyRescale(Orthanc::ImageAccessor& image,
+                                         bool useDouble) const
+  {
+    if (image.GetFormat() != Orthanc::PixelFormat_Float32)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
+    }
+    
+    if (hasRescale_)
+    {
+      for (unsigned int y = 0; y < image.GetHeight(); y++)
+      {
+        float* p = reinterpret_cast<float*>(image.GetRow(y));
+
+        if (useDouble)
+        {
+          // Slower, accurate implementation using double
+          for (unsigned int x = 0; x < image.GetWidth(); x++, p++)
+          {
+            double value = static_cast<double>(*p);
+            *p = static_cast<float>(value * rescaleSlope_ + rescaleIntercept_);
+          }
+        }
+        else
+        {
+          // Fast, approximate implementation using float
+          for (unsigned int x = 0; x < image.GetWidth(); x++, p++)
+          {
+            *p = (*p) * static_cast<float>(rescaleSlope_) + static_cast<float>(rescaleIntercept_);
+          }
+        }
+      }
+    }
+  }
+
+  
+  double DicomFrameConverter::Apply(double x) const
+  {
+    return x * rescaleSlope_ + rescaleIntercept_;
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Toolbox/DicomFrameConverter.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,169 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include <Plugins/Samples/Common/IDicomDataset.h>
+#include <Core/DicomFormat/DicomMap.h>
+#include <Core/Images/ImageAccessor.h>
+
+#include <memory>
+
+namespace Deprecated
+{
+  /**
+   * This class is responsible for converting the pixel format of a
+   * DICOM frame coming from Orthanc, into a pixel format that is
+   * suitable for Stone, given the relevant DICOM tags:
+   * - Color frames will stay in the RGB24 format.
+   * - Grayscale frames will be converted to the Float32 format.
+   **/
+  class DicomFrameConverter
+  {
+  private:
+    bool    isSigned_;
+    bool    isColor_;
+    bool    hasRescale_;
+    double  rescaleIntercept_;
+    double  rescaleSlope_;
+    bool    hasDefaultWindow_;
+    double  defaultWindowCenter_;
+    double  defaultWindowWidth_;
+    
+    Orthanc::PhotometricInterpretation  photometric_;
+    Orthanc::PixelFormat                expectedPixelFormat_;
+
+    void SetDefaultParameters();
+
+  public:
+    DicomFrameConverter()
+    {
+      SetDefaultParameters();
+    }
+
+    ~DicomFrameConverter()
+    {
+      // TODO: check whether this dtor is called or not
+      // An MSVC warning explains that declaring an
+      // std::auto_ptr with a forward-declared type
+      // prevents its dtor from being called. Does not
+      // seem an issue here (only POD types inside), but
+      // definitely something to keep an eye on.
+      (void)0;
+    }
+
+    // AM: this is required to serialize/deserialize it
+    DicomFrameConverter(
+        bool isSigned,
+        bool isColor,
+        bool hasRescale,
+        double rescaleIntercept,
+        double rescaleSlope,
+        bool hasDefaultWindow,
+        double defaultWindowCenter,
+        double defaultWindowWidth,
+        Orthanc::PhotometricInterpretation photometric,
+        Orthanc::PixelFormat expectedPixelFormat
+        ):
+      isSigned_(isSigned),
+      isColor_(isColor),
+      hasRescale_(hasRescale),
+      rescaleIntercept_(rescaleIntercept),
+      rescaleSlope_(rescaleSlope),
+      hasDefaultWindow_(hasDefaultWindow),
+      defaultWindowCenter_(defaultWindowCenter),
+      defaultWindowWidth_(defaultWindowWidth),
+      photometric_(photometric),
+      expectedPixelFormat_(expectedPixelFormat)
+    {}
+
+    void GetParameters(bool& isSigned,
+                       bool& isColor,
+                       bool& hasRescale,
+                       double& rescaleIntercept,
+                       double& rescaleSlope,
+                       bool& hasDefaultWindow,
+                       double& defaultWindowCenter,
+                       double& defaultWindowWidth,
+                       Orthanc::PhotometricInterpretation& photometric,
+                       Orthanc::PixelFormat& expectedPixelFormat) const
+    {
+      isSigned = isSigned_;
+      isColor = isColor_;
+      hasRescale = hasRescale_;
+      rescaleIntercept = rescaleIntercept_;
+      rescaleSlope = rescaleSlope_;
+      hasDefaultWindow = hasDefaultWindow_;
+      defaultWindowCenter = defaultWindowCenter_;
+      defaultWindowWidth = defaultWindowWidth_;
+      photometric = photometric_;
+      expectedPixelFormat = expectedPixelFormat_;
+    }
+
+    Orthanc::PixelFormat GetExpectedPixelFormat() const
+    {
+      return expectedPixelFormat_;
+    }
+
+    Orthanc::PhotometricInterpretation GetPhotometricInterpretation() const
+    {
+      return photometric_;
+    }
+
+    void ReadParameters(const Orthanc::DicomMap& dicom);
+
+    void ReadParameters(const OrthancPlugins::IDicomDataset& dicom);
+
+    bool HasDefaultWindow() const
+    {
+      return hasDefaultWindow_;
+    }
+    
+    double GetDefaultWindowCenter() const
+    {
+      return defaultWindowCenter_;
+    }
+    
+    double GetDefaultWindowWidth() const
+    {
+      return defaultWindowWidth_;
+    }
+
+    double GetRescaleIntercept() const
+    {
+      return rescaleIntercept_;
+    }
+
+    double GetRescaleSlope() const
+    {
+      return rescaleSlope_;
+    }
+
+    void ConvertFrameInplace(std::auto_ptr<Orthanc::ImageAccessor>& source) const;
+
+    Orthanc::ImageAccessor* ConvertFrame(const Orthanc::ImageAccessor& source) const;
+
+    void ApplyRescale(Orthanc::ImageAccessor& image,
+                      bool useDouble) const;
+
+    double Apply(double x) const;
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Toolbox/DownloadStack.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,196 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "DownloadStack.h"
+
+#include <Core/OrthancException.h>
+
+#include <cassert>
+
+namespace Deprecated
+{
+  bool DownloadStack::CheckInvariants() const
+  {
+    std::vector<bool> dequeued(nodes_.size(), true);
+
+    int i = firstNode_;
+    while (i != NIL)
+    {
+      const Node& node = nodes_[i];
+
+      dequeued[i] = false;
+
+      if (node.next_ != NIL &&
+          nodes_[node.next_].prev_ != i)
+      {
+        return false;
+      }
+
+      if (node.prev_ != NIL &&
+          nodes_[node.prev_].next_ != i)
+      {
+        return false;
+      }
+
+      i = nodes_[i].next_;
+    }
+
+    for (size_t i = 0; i < nodes_.size(); i++)
+    {
+      if (nodes_[i].dequeued_ != dequeued[i])
+      {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+
+  DownloadStack::DownloadStack(unsigned int size)
+  {
+    nodes_.resize(size);
+
+    if (size == 0)
+    {
+      firstNode_ = NIL;
+    }
+    else
+    {
+      for (size_t i = 0; i < size; i++)
+      {
+        nodes_[i].prev_ = static_cast<int>(i - 1);
+        nodes_[i].next_ = static_cast<int>(i + 1);
+        nodes_[i].dequeued_ = false;
+      }
+
+      nodes_.front().prev_ = NIL;
+      nodes_.back().next_ = NIL;
+      firstNode_ = 0;
+    }
+
+    assert(CheckInvariants());
+  }
+
+
+  DownloadStack::~DownloadStack()
+  {
+    assert(CheckInvariants());    
+  }
+
+
+  bool DownloadStack::Pop(unsigned int& value)
+  {
+    assert(CheckInvariants());
+
+    if (firstNode_ == NIL)
+    {
+      for (size_t i = 0; i < nodes_.size(); i++)
+      {
+        assert(nodes_[i].dequeued_);
+      }
+
+      return false;
+    }
+    else
+    {
+      assert(firstNode_ >= 0 && firstNode_ < static_cast<int>(nodes_.size()));
+      value = firstNode_;
+
+      Node& node = nodes_[firstNode_];
+      assert(node.prev_ == NIL);
+      assert(!node.dequeued_);
+
+      node.dequeued_ = true;
+      firstNode_ = node.next_;
+
+      if (firstNode_ != NIL)
+      {
+        nodes_[firstNode_].prev_ = NIL;
+      }
+
+      return true;
+    }
+  }
+
+
+  void DownloadStack::SetTopNodeInternal(unsigned int value)
+  {
+    assert(CheckInvariants());
+
+    Node& node = nodes_[value];
+
+    if (node.dequeued_)
+    {
+      // This node has already been processed by the download thread, nothing to do
+      return;
+    }
+
+    // Remove the node from the list
+    if (node.prev_ == NIL)
+    {
+      assert(firstNode_ == static_cast<int>(value));
+      
+      // This is already the top node in the list, nothing to do
+      return;
+    }
+
+    nodes_[node.prev_].next_ = node.next_;
+
+    if (node.next_ != NIL)
+    {
+      nodes_[node.next_].prev_ = node.prev_;
+    }
+
+    // Add back the node at the top of the list
+    assert(firstNode_ != NIL);
+
+    Node& old = nodes_[firstNode_];
+    assert(old.prev_ == NIL);
+    assert(!old.dequeued_);
+    node.prev_ = NIL;
+    node.next_ = firstNode_;
+    old.prev_ = value;
+
+    firstNode_ = value;
+  }
+
+  
+  void DownloadStack::SetTopNode(unsigned int value)
+  {
+    if (value >= nodes_.size())
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+
+    SetTopNodeInternal(value);
+  }
+
+
+  void DownloadStack::SetTopNodePermissive(int value)
+  {
+    if (value >= 0 &&
+        value < static_cast<int>(nodes_.size()))
+    {
+      SetTopNodeInternal(value);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Toolbox/DownloadStack.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,60 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include <vector>
+#include <boost/noncopyable.hpp>
+
+namespace Deprecated
+{
+  class DownloadStack : public boost::noncopyable
+  {
+  private:
+    static const int NIL = -1;
+
+    // This is a doubly-linked list
+    struct Node
+    {
+      int   next_;
+      int   prev_;
+      bool  dequeued_;
+    };
+
+    std::vector<Node>   nodes_;
+    int                 firstNode_;
+
+    bool CheckInvariants() const;
+
+    void SetTopNodeInternal(unsigned int value);  
+
+  public:
+    DownloadStack(unsigned int size);
+
+    ~DownloadStack();
+
+    bool Pop(unsigned int& value);
+
+    void SetTopNode(unsigned int value);  
+
+    void SetTopNodePermissive(int value);
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Toolbox/IDelayedCallExecutor.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,58 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2018 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 "../../Messages/IObserver.h"
+#include "../../Messages/ICallable.h"
+
+#include <Core/IDynamicObject.h>
+#include <Core/Logging.h>
+
+#include <string>
+#include <map>
+
+namespace Deprecated
+{
+  // The IDelayedCall executes a callback after a delay (equivalent to timeout() function in javascript).
+  class IDelayedCallExecutor : public boost::noncopyable
+  {
+  protected:
+    OrthancStone::MessageBroker& broker_;
+    
+  public:
+    ORTHANC_STONE_DEFINE_EMPTY_MESSAGE(__FILE__, __LINE__, TimeoutMessage);
+
+    IDelayedCallExecutor(OrthancStone::MessageBroker& broker) :
+      broker_(broker)
+    {
+    }
+
+    
+    virtual ~IDelayedCallExecutor()
+    {
+    }
+
+    
+    virtual void Schedule(OrthancStone::MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback,
+                          unsigned int timeoutInMs = 1000) = 0;
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Toolbox/IWebService.cpp	Wed May 22 16:13:46 2019 +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-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "IWebService.h"
+
+#include <Core/OrthancException.h>
+
+
+namespace Deprecated
+{
+  const Orthanc::IDynamicObject&
+  IWebService::HttpRequestSuccessMessage::GetPayload() const
+  {
+    if (HasPayload())
+    {
+      return *payload_;
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+  }
+    
+
+  const Orthanc::IDynamicObject&
+  IWebService::HttpRequestErrorMessage::GetPayload() const
+  {
+    if (HasPayload())
+    {
+      return *payload_;
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Toolbox/IWebService.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,166 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../../Messages/IObserver.h"
+#include "../../Messages/ICallable.h"
+
+#include <Core/IDynamicObject.h>
+#include <Core/Logging.h>
+
+#include <string>
+#include <map>
+
+namespace Deprecated
+{
+  // The IWebService performs HTTP requests.
+  // Since applications can run in native or WASM environment and, since
+  // in a WASM environment, the WebService is asynchronous, the IWebservice
+  // also implements an asynchronous interface: you must schedule a request
+  // and you'll be notified when the response/error is ready.
+  class IWebService : public boost::noncopyable
+  {
+  protected:
+    OrthancStone::MessageBroker& broker_;
+    
+  public:
+    typedef std::map<std::string, std::string> HttpHeaders;
+
+    class HttpRequestSuccessMessage : public OrthancStone::IMessage
+    {
+      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
+
+    private:
+      const std::string&             uri_;
+      const void*                    answer_;
+      size_t                         answerSize_;
+      const HttpHeaders&             answerHeaders_;
+      const Orthanc::IDynamicObject* payload_;
+
+    public:
+      HttpRequestSuccessMessage(const std::string& uri,
+                                const void* answer,
+                                size_t answerSize,
+                                const HttpHeaders& answerHeaders,
+                                const Orthanc::IDynamicObject* payload) :
+        uri_(uri),
+        answer_(answer),
+        answerSize_(answerSize),
+        answerHeaders_(answerHeaders),
+        payload_(payload)
+      {
+      }
+
+      const std::string& GetUri() const
+      {
+        return uri_;
+      }
+
+      const void* GetAnswer() const
+      {
+        return answer_;
+      }
+
+      size_t GetAnswerSize() const
+      {
+        return answerSize_;
+      }
+
+      const HttpHeaders&  GetAnswerHttpHeaders() const
+      {
+        return answerHeaders_;
+      }
+
+      bool HasPayload() const
+      {
+        return payload_ != NULL;
+      }
+
+      const Orthanc::IDynamicObject& GetPayload() const;
+    };
+    
+
+    class HttpRequestErrorMessage : public OrthancStone::IMessage
+    {
+      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
+
+    private:
+      const std::string&              uri_;
+      const Orthanc::IDynamicObject*  payload_;
+
+    public:
+      HttpRequestErrorMessage(const std::string& uri,
+                              const Orthanc::IDynamicObject* payload) :
+        uri_(uri),
+        payload_(payload)
+      {
+      }
+
+      const std::string& GetUri() const
+      {
+        return uri_;
+      }
+
+      bool HasPayload() const
+      {
+        return payload_ != NULL;
+      }
+
+      const Orthanc::IDynamicObject& GetPayload() const;
+    };
+
+
+    IWebService(OrthancStone::MessageBroker& broker) :
+      broker_(broker)
+    {
+    }
+
+    
+    virtual ~IWebService()
+    {
+    }
+
+    virtual void EnableCache(bool enable) = 0;
+    
+    virtual void GetAsync(const std::string& uri,
+                          const HttpHeaders& headers,
+                          Orthanc::IDynamicObject* payload  /* takes ownership */,
+                          OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,
+                          OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
+                          unsigned int timeoutInSeconds = 60) = 0;
+
+    virtual void PostAsync(const std::string& uri,
+                           const HttpHeaders& headers,
+                           const std::string& body,
+                           Orthanc::IDynamicObject* payload  /* takes ownership */,
+                           OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,
+                           OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
+                           unsigned int timeoutInSeconds = 60) = 0;
+
+    virtual void DeleteAsync(const std::string& uri,
+                             const HttpHeaders& headers,
+                             Orthanc::IDynamicObject* payload  /* takes ownership */,
+                             OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,
+                             OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
+                             unsigned int timeoutInSeconds = 60) = 0;
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Toolbox/OrthancApiClient.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,336 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "OrthancApiClient.h"
+
+#include "../../Toolbox/MessagingToolbox.h"
+
+#include <Core/OrthancException.h>
+
+namespace Deprecated
+{
+  const Orthanc::IDynamicObject& OrthancApiClient::JsonResponseReadyMessage::GetPayload() const
+  {
+    if (HasPayload())
+    {
+      return *payload_;
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+  }
+  
+  
+  const Orthanc::IDynamicObject& OrthancApiClient::BinaryResponseReadyMessage::GetPayload() const
+  {
+    if (HasPayload())
+    {
+      return *payload_;
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+  }
+  
+  
+  const Orthanc::IDynamicObject& OrthancApiClient::EmptyResponseReadyMessage::GetPayload() const
+  {
+    if (HasPayload())
+    {
+      return *payload_;
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+  }
+  
+  
+  class OrthancApiClient::WebServicePayload : public Orthanc::IDynamicObject
+  {
+  private:
+    std::auto_ptr< OrthancStone::MessageHandler<EmptyResponseReadyMessage> >             emptyHandler_;
+    std::auto_ptr< OrthancStone::MessageHandler<JsonResponseReadyMessage> >              jsonHandler_;
+    std::auto_ptr< OrthancStone::MessageHandler<BinaryResponseReadyMessage> >            binaryHandler_;
+    std::auto_ptr< OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage> >  failureHandler_;
+    std::auto_ptr< Orthanc::IDynamicObject >                               userPayload_;
+
+    void NotifyConversionError(const IWebService::HttpRequestSuccessMessage& message) const
+    {
+      if (failureHandler_.get() != NULL)
+      {
+        failureHandler_->Apply(IWebService::HttpRequestErrorMessage
+                               (message.GetUri(), userPayload_.get()));
+      }
+    }
+    
+  public:
+    WebServicePayload(OrthancStone::MessageHandler<EmptyResponseReadyMessage>* handler,
+                      OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureHandler,
+                      Orthanc::IDynamicObject* userPayload) :
+      emptyHandler_(handler),
+      failureHandler_(failureHandler),
+      userPayload_(userPayload)
+    {
+      if (handler == NULL)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+      }
+    }
+
+    WebServicePayload(OrthancStone::MessageHandler<BinaryResponseReadyMessage>* handler,
+                      OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureHandler,
+                      Orthanc::IDynamicObject* userPayload) :
+      binaryHandler_(handler),
+      failureHandler_(failureHandler),
+      userPayload_(userPayload)
+    {
+      if (handler == NULL)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+      }
+    }
+
+    WebServicePayload(OrthancStone::MessageHandler<JsonResponseReadyMessage>* handler,
+                      OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureHandler,
+                      Orthanc::IDynamicObject* userPayload) :
+      jsonHandler_(handler),
+      failureHandler_(failureHandler),
+      userPayload_(userPayload)
+    {
+      if (handler == NULL)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+      }
+    }
+
+    void HandleSuccess(const IWebService::HttpRequestSuccessMessage& message) const
+    {
+      if (emptyHandler_.get() != NULL)
+      {
+        emptyHandler_->Apply(OrthancApiClient::EmptyResponseReadyMessage
+                             (message.GetUri(), userPayload_.get()));
+      }
+      else if (binaryHandler_.get() != NULL)
+      {
+        binaryHandler_->Apply(OrthancApiClient::BinaryResponseReadyMessage
+                              (message.GetUri(), message.GetAnswer(),
+                               message.GetAnswerSize(), userPayload_.get()));
+      }
+      else if (jsonHandler_.get() != NULL)
+      {
+        Json::Value response;
+        if (OrthancStone::MessagingToolbox::ParseJson(response, message.GetAnswer(), message.GetAnswerSize()))
+        {
+          jsonHandler_->Apply(OrthancApiClient::JsonResponseReadyMessage
+                              (message.GetUri(), response, userPayload_.get()));
+        }
+        else
+        {
+          NotifyConversionError(message);
+        }
+      }
+      else
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      }
+    }
+
+    void HandleFailure(const IWebService::HttpRequestErrorMessage& message) const
+    {
+      if (failureHandler_.get() != NULL)
+      {
+        failureHandler_->Apply(IWebService::HttpRequestErrorMessage
+                               (message.GetUri(), userPayload_.get()));
+      }
+    }
+  };
+
+
+  OrthancApiClient::OrthancApiClient(OrthancStone::MessageBroker& broker,
+                                     IWebService& web,
+                                     const std::string& baseUrl) :
+    IObservable(broker),
+    IObserver(broker),
+    web_(web),
+    baseUrl_(baseUrl)
+  {
+  }
+
+
+  void OrthancApiClient::GetJsonAsync(
+      const std::string& uri,
+      OrthancStone::MessageHandler<JsonResponseReadyMessage>* successCallback,
+      OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,
+      Orthanc::IDynamicObject* payload)
+  {
+    IWebService::HttpHeaders emptyHeaders;
+    web_.GetAsync(baseUrl_ + uri,
+                  emptyHeaders,
+                  new WebServicePayload(successCallback, failureCallback, payload),
+                  new OrthancStone::Callable<OrthancApiClient, IWebService::HttpRequestSuccessMessage>
+                  (*this, &OrthancApiClient::NotifyHttpSuccess),
+                  new OrthancStone::Callable<OrthancApiClient, IWebService::HttpRequestErrorMessage>
+                  (*this, &OrthancApiClient::NotifyHttpError));
+  }
+
+
+  void OrthancApiClient::GetBinaryAsync(
+      const std::string& uri,
+      const std::string& contentType,
+      OrthancStone::MessageHandler<BinaryResponseReadyMessage>* successCallback,
+      OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,
+      Orthanc::IDynamicObject* payload)
+  {
+    IWebService::HttpHeaders headers;
+    headers["Accept"] = contentType;
+    GetBinaryAsync(uri, headers, successCallback, failureCallback, payload);
+  }
+
+  void OrthancApiClient::GetBinaryAsync(
+      const std::string& uri,
+      const IWebService::HttpHeaders& headers,
+      OrthancStone::MessageHandler<BinaryResponseReadyMessage>* successCallback,
+      OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,
+      Orthanc::IDynamicObject* payload)
+  {
+    // printf("GET [%s] [%s]\n", baseUrl_.c_str(), uri.c_str());
+
+    web_.GetAsync(baseUrl_ + uri, headers,
+                  new WebServicePayload(successCallback, failureCallback, payload),
+                  new OrthancStone::Callable<OrthancApiClient, IWebService::HttpRequestSuccessMessage>
+                  (*this, &OrthancApiClient::NotifyHttpSuccess),
+                  new OrthancStone::Callable<OrthancApiClient, IWebService::HttpRequestErrorMessage>
+                  (*this, &OrthancApiClient::NotifyHttpError));
+  }
+
+  
+  void OrthancApiClient::PostBinaryAsyncExpectJson(
+      const std::string& uri,
+      const std::string& body,
+      OrthancStone::MessageHandler<JsonResponseReadyMessage>* successCallback,
+      OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,
+      Orthanc::IDynamicObject* payload)
+  {
+    web_.PostAsync(baseUrl_ + uri, IWebService::HttpHeaders(), body,
+                   new WebServicePayload(successCallback, failureCallback, payload),
+                   new OrthancStone::Callable<OrthancApiClient, IWebService::HttpRequestSuccessMessage>
+                   (*this, &OrthancApiClient::NotifyHttpSuccess),
+                   new OrthancStone::Callable<OrthancApiClient, IWebService::HttpRequestErrorMessage>
+                   (*this, &OrthancApiClient::NotifyHttpError));
+
+  }
+
+  void OrthancApiClient::PostBinaryAsync(
+      const std::string& uri,
+      const std::string& body)
+  {
+    web_.PostAsync(baseUrl_ + uri, IWebService::HttpHeaders(), body, NULL, NULL, NULL);
+  }
+
+  void OrthancApiClient::PostBinaryAsync(
+      const std::string& uri,
+      const std::string& body,
+      OrthancStone::MessageHandler<EmptyResponseReadyMessage>* successCallback,
+      OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,
+      Orthanc::IDynamicObject* payload   /* takes ownership */)
+  {
+    web_.PostAsync(baseUrl_ + uri, IWebService::HttpHeaders(), body,
+                   new WebServicePayload(successCallback, failureCallback, payload),
+                   new OrthancStone::Callable<OrthancApiClient, IWebService::HttpRequestSuccessMessage>
+                   (*this, &OrthancApiClient::NotifyHttpSuccess),
+                   new OrthancStone::Callable<OrthancApiClient, IWebService::HttpRequestErrorMessage>
+                   (*this, &OrthancApiClient::NotifyHttpError));
+  }
+
+  void OrthancApiClient::PostJsonAsyncExpectJson(
+      const std::string& uri,
+      const Json::Value& data,
+      OrthancStone::MessageHandler<JsonResponseReadyMessage>* successCallback,
+      OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,
+      Orthanc::IDynamicObject* payload)
+  {
+    std::string body;
+    OrthancStone::MessagingToolbox::JsonToString(body, data);
+    return PostBinaryAsyncExpectJson(uri, body, successCallback, failureCallback, payload);
+  }
+
+  void OrthancApiClient::PostJsonAsync(
+      const std::string& uri,
+      const Json::Value& data)
+  {
+    std::string body;
+    OrthancStone::MessagingToolbox::JsonToString(body, data);
+    return PostBinaryAsync(uri, body);
+  }
+
+  void OrthancApiClient::PostJsonAsync(
+      const std::string& uri,
+      const Json::Value& data,
+      OrthancStone::MessageHandler<EmptyResponseReadyMessage>* successCallback,
+      OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,
+      Orthanc::IDynamicObject* payload   /* takes ownership */)
+  {
+    std::string body;
+    OrthancStone::MessagingToolbox::JsonToString(body, data);
+    return PostBinaryAsync(uri, body, successCallback, failureCallback, payload);
+  }
+
+  void OrthancApiClient::DeleteAsync(
+      const std::string& uri,
+      OrthancStone::MessageHandler<EmptyResponseReadyMessage>* successCallback,
+      OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,
+      Orthanc::IDynamicObject* payload)
+  {
+    web_.DeleteAsync(baseUrl_ + uri, IWebService::HttpHeaders(),
+                     new WebServicePayload(successCallback, failureCallback, payload),
+                     new OrthancStone::Callable<OrthancApiClient, IWebService::HttpRequestSuccessMessage>
+                     (*this, &OrthancApiClient::NotifyHttpSuccess),
+                     new OrthancStone::Callable<OrthancApiClient, IWebService::HttpRequestErrorMessage>
+                     (*this, &OrthancApiClient::NotifyHttpError));
+  }
+
+
+  void OrthancApiClient::NotifyHttpSuccess(const IWebService::HttpRequestSuccessMessage& message)
+  {
+    if (message.HasPayload())
+    {
+      dynamic_cast<const WebServicePayload&>(message.GetPayload()).HandleSuccess(message);
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+    }
+  }
+
+  void OrthancApiClient::NotifyHttpError(const IWebService::HttpRequestErrorMessage& message)
+  {
+    if (message.HasPayload())
+    {
+      dynamic_cast<const WebServicePayload&>(message.GetPayload()).HandleFailure(message);
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Toolbox/OrthancApiClient.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,243 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <json/json.h>
+
+#include "IWebService.h"
+#include "../../Messages/IObservable.h"
+#include "../../Messages/Promise.h"
+
+namespace Deprecated
+{
+  class OrthancApiClient :
+      public OrthancStone::IObservable,
+      public OrthancStone::IObserver
+  {
+  public:
+    class JsonResponseReadyMessage : public OrthancStone::IMessage
+    {
+      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
+
+    private:
+      const std::string&              uri_;
+      const Json::Value&              json_;
+      const Orthanc::IDynamicObject*  payload_;
+
+    public:
+      JsonResponseReadyMessage(const std::string& uri,
+                               const Json::Value& json,
+                               const Orthanc::IDynamicObject* payload) :
+        uri_(uri),
+        json_(json),
+        payload_(payload)
+      {
+      }
+
+      const std::string& GetUri() const
+      {
+        return uri_;
+      }
+
+      const Json::Value& GetJson() const
+      {
+        return json_;
+      }
+
+      bool HasPayload() const
+      {
+        return payload_ != NULL;
+      }
+
+      const Orthanc::IDynamicObject& GetPayload() const;
+    };
+    
+
+    class BinaryResponseReadyMessage : public OrthancStone::IMessage
+    {
+      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
+
+    private:
+      const std::string&              uri_;
+      const void*                     answer_;
+      size_t                          answerSize_;
+      const Orthanc::IDynamicObject*  payload_;
+
+    public:
+      BinaryResponseReadyMessage(const std::string& uri,
+                                 const void* answer,
+                                 size_t answerSize,
+                                 const Orthanc::IDynamicObject* payload) :
+        uri_(uri),
+        answer_(answer),
+        answerSize_(answerSize),
+        payload_(payload)
+      {
+      }
+
+      const std::string& GetUri() const
+      {
+        return uri_;
+      }
+
+      const void* GetAnswer() const
+      {
+        return answer_;
+      }
+
+      size_t GetAnswerSize() const
+      {
+        return answerSize_;
+      }
+
+      bool HasPayload() const
+      {
+        return payload_ != NULL;
+      }
+
+      const Orthanc::IDynamicObject& GetPayload() const;
+    };
+
+
+    class EmptyResponseReadyMessage : public OrthancStone::IMessage
+    {
+      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
+
+    private:
+      const std::string&              uri_;
+      const Orthanc::IDynamicObject*  payload_;
+
+    public:
+      EmptyResponseReadyMessage(const std::string& uri,
+                                const Orthanc::IDynamicObject* payload) :
+        uri_(uri),
+        payload_(payload)
+      {
+      }
+
+      const std::string& GetUri() const
+      {
+        return uri_;
+      }
+
+      bool HasPayload() const
+      {
+        return payload_ != NULL;
+      }
+
+      const Orthanc::IDynamicObject& GetPayload() const;
+    };
+
+    
+
+  private:
+    class WebServicePayload;
+
+  protected:
+    IWebService&  web_;
+    std::string   baseUrl_;
+
+  public:
+    OrthancApiClient(OrthancStone::MessageBroker& broker,
+                     IWebService& web,
+                     const std::string& baseUrl);
+    
+    virtual ~OrthancApiClient()
+    {
+    }
+
+    const std::string& GetBaseUrl() const {return baseUrl_;}
+
+    // schedule a GET request expecting a JSON response.
+    void GetJsonAsync(const std::string& uri,
+                      OrthancStone::MessageHandler<JsonResponseReadyMessage>* successCallback,
+                      OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
+                      Orthanc::IDynamicObject* payload = NULL   /* takes ownership */);
+
+    // schedule a GET request expecting a binary response.
+    void GetBinaryAsync(const std::string& uri,
+                        const std::string& contentType,
+                        OrthancStone::MessageHandler<BinaryResponseReadyMessage>* successCallback,
+                        OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
+                        Orthanc::IDynamicObject* payload = NULL   /* takes ownership */);
+
+    // schedule a GET request expecting a binary response.
+    void GetBinaryAsync(const std::string& uri,
+                        const IWebService::HttpHeaders& headers,
+                        OrthancStone::MessageHandler<BinaryResponseReadyMessage>* successCallback,
+                        OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
+                        Orthanc::IDynamicObject* payload = NULL   /* takes ownership */);
+
+    // schedule a POST request expecting a JSON response.
+    void PostBinaryAsyncExpectJson(const std::string& uri,
+                                   const std::string& body,
+                                   OrthancStone::MessageHandler<JsonResponseReadyMessage>* successCallback,
+                                   OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
+                                   Orthanc::IDynamicObject* payload = NULL   /* takes ownership */);
+
+    // schedule a POST request expecting a JSON response.
+    void PostJsonAsyncExpectJson(const std::string& uri,
+                                 const Json::Value& data,
+                                 OrthancStone::MessageHandler<JsonResponseReadyMessage>* successCallback,
+                                 OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
+                                 Orthanc::IDynamicObject* payload = NULL   /* takes ownership */);
+
+    // schedule a POST request and don't mind the response.
+    void PostJsonAsync(const std::string& uri,
+                       const Json::Value& data);
+
+    // schedule a POST request and don't expect any response.
+    void PostJsonAsync(const std::string& uri,
+                       const Json::Value& data,
+                       OrthancStone::MessageHandler<EmptyResponseReadyMessage>* successCallback,
+                       OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
+                       Orthanc::IDynamicObject* payload = NULL   /* takes ownership */);
+
+
+    // schedule a POST request and don't mind the response.
+    void PostBinaryAsync(const std::string& uri,
+                         const std::string& body);
+
+    // schedule a POST request and don't expect any response.
+    void PostBinaryAsync(const std::string& uri,
+                         const std::string& body,
+                         OrthancStone::MessageHandler<EmptyResponseReadyMessage>* successCallback,
+                         OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
+                         Orthanc::IDynamicObject* payload = NULL   /* takes ownership */);
+
+    // schedule a DELETE request expecting an empty response.
+    void DeleteAsync(const std::string& uri,
+                     OrthancStone::MessageHandler<EmptyResponseReadyMessage>* successCallback,
+                     OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
+                     Orthanc::IDynamicObject* payload = NULL   /* takes ownership */);
+
+    void NotifyHttpSuccess(const IWebService::HttpRequestSuccessMessage& message);
+
+    void NotifyHttpError(const IWebService::HttpRequestErrorMessage& message);
+
+  private:
+    void HandleFromCache(const std::string& uri,
+                         const IWebService::HttpHeaders& headers,
+                         Orthanc::IDynamicObject* payload /* takes ownership */);
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Toolbox/OrthancSlicesLoader.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,904 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "OrthancSlicesLoader.h"
+
+#include "../../Toolbox/MessagingToolbox.h"
+
+#include <Core/Compression/GzipCompressor.h>
+#include <Core/Endianness.h>
+#include <Core/Images/Image.h>
+#include <Core/Images/ImageProcessing.h>
+#include <Core/Images/JpegReader.h>
+#include <Core/Images/PngReader.h>
+#include <Core/Images/PamReader.h>
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+#include <Core/Toolbox.h>
+#include <Plugins/Samples/Common/FullOrthancDataset.h>
+
+#include <boost/lexical_cast.hpp>
+
+
+
+/**
+ * TODO This is a SLOW implementation of base64 decoding, because
+ * "Orthanc::Toolbox::DecodeBase64()" does not work properly with
+ * WASM. UNDERSTAND WHY.
+ * https://stackoverflow.com/a/34571089/881731
+ **/
+static std::string base64_decode(const std::string &in)
+{
+  std::string out;
+  
+  std::vector<int> T(256,-1);
+  for (int i=0; i<64; i++) T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i;
+  
+  int val=0, valb=-8;
+  for (size_t i = 0; i < in.size(); i++) {
+    unsigned char c = in[i];
+    if (T[c] == -1) break;
+    val = (val<<6) + T[c];
+    valb += 6;
+    if (valb>=0) {
+      out.push_back(char((val>>valb)&0xFF));
+      valb-=8;
+    }
+  }
+  return out;
+}
+
+
+
+namespace Deprecated
+{
+  class OrthancSlicesLoader::Operation : public Orthanc::IDynamicObject
+  {
+  private:
+    Mode               mode_;
+    unsigned int       frame_;
+    unsigned int       sliceIndex_;
+    const Slice*       slice_;
+    std::string        instanceId_;
+    SliceImageQuality  quality_;
+
+    Operation(Mode mode) :
+      mode_(mode)
+    {
+    }
+
+  public:
+    Mode GetMode() const
+    {
+      return mode_;
+    }
+
+    SliceImageQuality GetQuality() const
+    {
+      assert(mode_ == Mode_LoadImage ||
+             mode_ == Mode_LoadRawImage);
+      return quality_;
+    }
+
+    unsigned int GetSliceIndex() const
+    {
+      assert(mode_ == Mode_LoadImage ||
+             mode_ == Mode_LoadRawImage);
+      return sliceIndex_;
+    }
+
+    const Slice& GetSlice() const
+    {
+      assert(mode_ == Mode_LoadImage ||
+             mode_ == Mode_LoadRawImage);
+      assert(slice_ != NULL);
+      return *slice_;
+    }
+
+    unsigned int GetFrame() const
+    {
+      assert(mode_ == Mode_FrameGeometry);
+      return frame_;
+    }
+
+    const std::string& GetInstanceId() const
+    {
+      assert(mode_ == Mode_FrameGeometry ||
+             mode_ == Mode_InstanceGeometry);
+      return instanceId_;
+    }
+
+    static Operation* DownloadInstanceGeometry(const std::string& instanceId)
+    {
+      std::auto_ptr<Operation> operation(new Operation(Mode_InstanceGeometry));
+      operation->instanceId_ = instanceId;
+      return operation.release();
+    }
+
+    static Operation* DownloadFrameGeometry(const std::string& instanceId,
+                                            unsigned int frame)
+    {
+      std::auto_ptr<Operation> operation(new Operation(Mode_FrameGeometry));
+      operation->instanceId_ = instanceId;
+      operation->frame_ = frame;
+      return operation.release();
+    }
+
+    static Operation* DownloadSliceImage(unsigned int  sliceIndex,
+                                         const Slice&  slice,
+                                         SliceImageQuality quality)
+    {
+      std::auto_ptr<Operation> tmp(new Operation(Mode_LoadImage));
+      tmp->sliceIndex_ = sliceIndex;
+      tmp->slice_ = &slice;
+      tmp->quality_ = quality;
+      return tmp.release();
+    }
+
+    static Operation* DownloadSliceRawImage(unsigned int  sliceIndex,
+                                            const Slice&  slice)
+    {
+      std::auto_ptr<Operation> tmp(new Operation(Mode_LoadRawImage));
+      tmp->sliceIndex_ = sliceIndex;
+      tmp->slice_ = &slice;
+      tmp->quality_ = SliceImageQuality_InternalRaw;
+      return tmp.release();
+    }
+
+    static Operation* DownloadDicomFile(const Slice&  slice)
+    {
+      std::auto_ptr<Operation> tmp(new Operation(Mode_LoadDicomFile));
+      tmp->slice_ = &slice;
+      return tmp.release();
+    }
+
+  };
+
+  void OrthancSlicesLoader::NotifySliceImageSuccess(const Operation& operation,
+                                                    const Orthanc::ImageAccessor& image)
+  {
+    OrthancSlicesLoader::SliceImageReadyMessage msg
+      (*this, operation.GetSliceIndex(), operation.GetSlice(), image, operation.GetQuality());
+    BroadcastMessage(msg);
+  }
+  
+  
+  void OrthancSlicesLoader::NotifySliceImageError(const Operation& operation)
+  {
+    OrthancSlicesLoader::SliceImageErrorMessage msg
+      (*this, operation.GetSliceIndex(), operation.GetSlice(), operation.GetQuality());
+    BroadcastMessage(msg);
+  }
+  
+  
+  void OrthancSlicesLoader::SortAndFinalizeSlices()
+  {
+    bool ok = slices_.Sort();
+    
+    state_ = State_GeometryReady;
+    
+    if (ok)
+    {
+      LOG(INFO) << "Loaded a series with " << slices_.GetSlicesCount() << " slice(s)";
+      BroadcastMessage(SliceGeometryReadyMessage(*this));
+    }
+    else
+    {
+      LOG(ERROR) << "This series is empty";
+      BroadcastMessage(SliceGeometryErrorMessage(*this));
+    }
+  }
+  
+  void OrthancSlicesLoader::OnGeometryError(const IWebService::HttpRequestErrorMessage& message)
+  {
+    BroadcastMessage(SliceGeometryErrorMessage(*this));
+    state_ = State_Error;
+  }
+
+  void OrthancSlicesLoader::OnSliceImageError(const IWebService::HttpRequestErrorMessage& message)
+  {
+    NotifySliceImageError(dynamic_cast<const Operation&>(message.GetPayload()));
+    state_ = State_Error;
+  }
+
+  void OrthancSlicesLoader::ParseSeriesGeometry(const OrthancApiClient::JsonResponseReadyMessage& message)
+  {
+    const Json::Value& series = message.GetJson();
+    Json::Value::Members instances = series.getMemberNames();
+    
+    slices_.Reserve(instances.size());
+    
+    for (size_t i = 0; i < instances.size(); i++)
+    {
+      OrthancPlugins::FullOrthancDataset dataset(series[instances[i]]);
+      
+      Orthanc::DicomMap dicom;
+      OrthancStone::MessagingToolbox::ConvertDataset(dicom, dataset);
+      
+      unsigned int frames;
+      if (!dicom.ParseUnsignedInteger32(frames, Orthanc::DICOM_TAG_NUMBER_OF_FRAMES))
+      {
+        frames = 1;
+      }
+      
+      for (unsigned int frame = 0; frame < frames; frame++)
+      {
+        std::auto_ptr<Slice> slice(new Slice);
+        if (slice->ParseOrthancFrame(dicom, instances[i], frame))
+        {
+          OrthancStone::CoordinateSystem3D geometry = slice->GetGeometry();
+          slices_.AddSlice(geometry, slice.release());
+        }
+        else
+        {
+          LOG(WARNING) << "Skipping invalid frame " << frame << " within instance " << instances[i];
+        }
+      }
+    }
+    
+    SortAndFinalizeSlices();
+  }
+  
+  void OrthancSlicesLoader::ParseInstanceGeometry(const OrthancApiClient::JsonResponseReadyMessage& message)
+  {
+    const Json::Value& tags = message.GetJson();
+    const std::string& instanceId = dynamic_cast<const OrthancSlicesLoader::Operation&>(message.GetPayload()).GetInstanceId();
+
+    OrthancPlugins::FullOrthancDataset dataset(tags);
+    
+    Orthanc::DicomMap dicom;
+    OrthancStone::MessagingToolbox::ConvertDataset(dicom, dataset);
+
+    unsigned int frames;
+    if (!dicom.ParseUnsignedInteger32(frames, Orthanc::DICOM_TAG_NUMBER_OF_FRAMES))
+    {
+      frames = 1;
+    }
+    
+    LOG(INFO) << "Instance " << instanceId << " contains " << frames << " frame(s)";
+    
+    for (unsigned int frame = 0; frame < frames; frame++)
+    {
+      std::auto_ptr<Slice> slice(new Slice);
+      if (slice->ParseOrthancFrame(dicom, instanceId, frame))
+      {
+        OrthancStone::CoordinateSystem3D geometry = slice->GetGeometry();
+        slices_.AddSlice(geometry, slice.release());
+      }
+      else
+      {
+        LOG(WARNING) << "Skipping invalid multi-frame instance " << instanceId;
+        BroadcastMessage(SliceGeometryErrorMessage(*this));
+        return;
+      }
+    }
+    
+    SortAndFinalizeSlices();
+  }
+  
+  
+  void OrthancSlicesLoader::ParseFrameGeometry(const OrthancApiClient::JsonResponseReadyMessage& message)
+  {
+    const Json::Value& tags = message.GetJson();
+    const std::string& instanceId = dynamic_cast<const OrthancSlicesLoader::Operation&>(message.GetPayload()).GetInstanceId();
+    unsigned int frame = dynamic_cast<const OrthancSlicesLoader::Operation&>(message.GetPayload()).GetFrame();
+
+    OrthancPlugins::FullOrthancDataset dataset(tags);
+    
+    state_ = State_GeometryReady;
+    
+    Orthanc::DicomMap dicom;
+    OrthancStone::MessagingToolbox::ConvertDataset(dicom, dataset);
+    
+    std::auto_ptr<Slice> slice(new Slice);
+    if (slice->ParseOrthancFrame(dicom, instanceId, frame))
+    {
+      LOG(INFO) << "Loaded instance geometry " << instanceId;
+
+      OrthancStone::CoordinateSystem3D geometry = slice->GetGeometry();
+      slices_.AddSlice(geometry, slice.release());
+      
+      BroadcastMessage(SliceGeometryReadyMessage(*this));
+    }
+    else
+    {
+      LOG(WARNING) << "Skipping invalid instance " << instanceId;
+      BroadcastMessage(SliceGeometryErrorMessage(*this));
+    }
+  }
+  
+  
+  void OrthancSlicesLoader::ParseSliceImagePng(const OrthancApiClient::BinaryResponseReadyMessage& message)
+  {
+    const Operation& operation = dynamic_cast<const OrthancSlicesLoader::Operation&>(message.GetPayload());
+    std::auto_ptr<Orthanc::ImageAccessor>  image;
+    
+    try
+    {
+      image.reset(new Orthanc::PngReader);
+      dynamic_cast<Orthanc::PngReader&>(*image).ReadFromMemory(message.GetAnswer(), message.GetAnswerSize());
+    }
+    catch (Orthanc::OrthancException&)
+    {
+      NotifySliceImageError(operation);
+      return;
+    }
+    
+    if (image->GetWidth() != operation.GetSlice().GetWidth() ||
+        image->GetHeight() != operation.GetSlice().GetHeight())
+    {
+      NotifySliceImageError(operation);
+      return;
+    }
+
+    if (operation.GetSlice().GetConverter().GetExpectedPixelFormat() ==
+        Orthanc::PixelFormat_SignedGrayscale16)
+    {
+      if (image->GetFormat() == Orthanc::PixelFormat_Grayscale16)
+      {
+        image->SetFormat(Orthanc::PixelFormat_SignedGrayscale16);
+      }
+      else
+      {
+        NotifySliceImageError(operation);
+        return;
+      }
+    }
+    
+    NotifySliceImageSuccess(operation, *image);
+  }
+  
+  void OrthancSlicesLoader::ParseSliceImagePam(const OrthancApiClient::BinaryResponseReadyMessage& message)
+  {
+    const Operation& operation = dynamic_cast<const OrthancSlicesLoader::Operation&>(message.GetPayload());
+    std::auto_ptr<Orthanc::ImageAccessor>  image;
+
+    try
+    {
+      image.reset(new Orthanc::PamReader);
+      dynamic_cast<Orthanc::PamReader&>(*image).ReadFromMemory(message.GetAnswer(), message.GetAnswerSize());
+    }
+    catch (Orthanc::OrthancException&)
+    {
+      NotifySliceImageError(operation);
+      return;
+    }
+
+    if (image->GetWidth() != operation.GetSlice().GetWidth() ||
+        image->GetHeight() != operation.GetSlice().GetHeight())
+    {
+      NotifySliceImageError(operation);
+      return;
+    }
+
+    if (operation.GetSlice().GetConverter().GetExpectedPixelFormat() ==
+        Orthanc::PixelFormat_SignedGrayscale16)
+    {
+      if (image->GetFormat() == Orthanc::PixelFormat_Grayscale16)
+      {
+        image->SetFormat(Orthanc::PixelFormat_SignedGrayscale16);
+      }
+      else
+      {
+        NotifySliceImageError(operation);
+        return;
+      }
+    }
+
+    NotifySliceImageSuccess(operation, *image);
+  }
+
+
+  void OrthancSlicesLoader::ParseSliceImageJpeg(const OrthancApiClient::JsonResponseReadyMessage& message)
+  {
+    const Operation& operation = dynamic_cast<const OrthancSlicesLoader::Operation&>(message.GetPayload());
+
+    const Json::Value& encoded = message.GetJson();
+    if (encoded.type() != Json::objectValue ||
+        !encoded.isMember("Orthanc") ||
+        encoded["Orthanc"].type() != Json::objectValue)
+    {
+      NotifySliceImageError(operation);
+      return;
+    }
+    
+    const Json::Value& info = encoded["Orthanc"];
+    if (!info.isMember("PixelData") ||
+        !info.isMember("Stretched") ||
+        !info.isMember("Compression") ||
+        info["Compression"].type() != Json::stringValue ||
+        info["PixelData"].type() != Json::stringValue ||
+        info["Stretched"].type() != Json::booleanValue ||
+        info["Compression"].asString() != "Jpeg")
+    {
+      NotifySliceImageError(operation);
+      return;
+    }
+    
+    bool isSigned = false;
+    bool isStretched = info["Stretched"].asBool();
+    
+    if (info.isMember("IsSigned"))
+    {
+      if (info["IsSigned"].type() != Json::booleanValue)
+      {
+        NotifySliceImageError(operation);
+        return;
+      }
+      else
+      {
+        isSigned = info["IsSigned"].asBool();
+      }
+    }
+    
+    std::auto_ptr<Orthanc::ImageAccessor> reader;
+    
+    {
+      std::string jpeg;
+      //Orthanc::Toolbox::DecodeBase64(jpeg, info["PixelData"].asString());
+      jpeg = base64_decode(info["PixelData"].asString());
+      
+      try
+      {
+        reader.reset(new Orthanc::JpegReader);
+        dynamic_cast<Orthanc::JpegReader&>(*reader).ReadFromMemory(jpeg);
+      }
+      catch (Orthanc::OrthancException&)
+      {
+        NotifySliceImageError(operation);
+        return;
+      }
+    }
+    
+    Orthanc::PixelFormat expectedFormat =
+      operation.GetSlice().GetConverter().GetExpectedPixelFormat();
+    
+    if (reader->GetFormat() == Orthanc::PixelFormat_RGB24)  // This is a color image
+    {
+      if (expectedFormat != Orthanc::PixelFormat_RGB24)
+      {
+        NotifySliceImageError(operation);
+        return;
+      }
+      
+      if (isSigned || isStretched)
+      {
+        NotifySliceImageError(operation);
+        return;
+      }
+      else
+      {
+        NotifySliceImageSuccess(operation, *reader);
+        return;
+      }
+    }
+    
+    if (reader->GetFormat() != Orthanc::PixelFormat_Grayscale8)
+    {
+      NotifySliceImageError(operation);
+      return;
+    }
+    
+    if (!isStretched)
+    {
+      if (expectedFormat != reader->GetFormat())
+      {
+        NotifySliceImageError(operation);
+        return;
+      }
+      else
+      {
+        NotifySliceImageSuccess(operation, *reader);
+        return;
+      }
+    }
+    
+    int32_t stretchLow = 0;
+    int32_t stretchHigh = 0;
+    
+    if (!info.isMember("StretchLow") ||
+        !info.isMember("StretchHigh") ||
+        info["StretchLow"].type() != Json::intValue ||
+        info["StretchHigh"].type() != Json::intValue)
+    {
+      NotifySliceImageError(operation);
+      return;
+    }
+    
+    stretchLow = info["StretchLow"].asInt();
+    stretchHigh = info["StretchHigh"].asInt();
+    
+    if (stretchLow < -32768 ||
+        stretchHigh > 65535 ||
+        (stretchLow < 0 && stretchHigh > 32767))
+    {
+      // This range cannot be represented with a uint16_t or an int16_t
+      NotifySliceImageError(operation);
+      return;
+    }
+    
+    // Decode a grayscale JPEG 8bpp image coming from the Web viewer
+    std::auto_ptr<Orthanc::ImageAccessor> image
+      (new Orthanc::Image(expectedFormat, reader->GetWidth(), reader->GetHeight(), false));
+
+    Orthanc::ImageProcessing::Convert(*image, *reader);
+    reader.reset();
+    
+    float scaling = static_cast<float>(stretchHigh - stretchLow) / 255.0f;
+    
+    if (!OrthancStone::LinearAlgebra::IsCloseToZero(scaling))
+    {
+      float offset = static_cast<float>(stretchLow) / scaling;
+      Orthanc::ImageProcessing::ShiftScale(*image, offset, scaling, true);
+    }
+    
+    NotifySliceImageSuccess(operation, *image);
+  }
+
+
+  class StringImage : public Orthanc::ImageAccessor
+  {
+  private:
+    std::string  buffer_;
+    
+  public:
+    StringImage(Orthanc::PixelFormat format,
+                unsigned int width,
+                unsigned int height,
+                std::string& buffer)
+    {
+      if (buffer.size() != Orthanc::GetBytesPerPixel(format) * width * height)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
+      }
+      
+      buffer_.swap(buffer);  // The source buffer is now empty
+      
+      void* data = (buffer_.empty() ? NULL : &buffer_[0]);
+      
+      AssignWritable(format, width, height,
+                     Orthanc::GetBytesPerPixel(format) * width, data);
+    }
+  };
+  
+  void OrthancSlicesLoader::ParseSliceRawImage(const OrthancApiClient::BinaryResponseReadyMessage& message)
+  {
+    const Operation& operation = dynamic_cast<const OrthancSlicesLoader::Operation&>(message.GetPayload());
+    Orthanc::GzipCompressor compressor;
+    
+    std::string raw;
+    compressor.Uncompress(raw, message.GetAnswer(), message.GetAnswerSize());
+    
+    const Orthanc::DicomImageInformation& info = operation.GetSlice().GetImageInformation();
+    
+    if (info.GetBitsAllocated() == 32 &&
+        info.GetBitsStored() == 32 &&
+        info.GetHighBit() == 31 &&
+        info.GetChannelCount() == 1 &&
+        !info.IsSigned() &&
+        info.GetPhotometricInterpretation() == Orthanc::PhotometricInterpretation_Monochrome2 &&
+        raw.size() == info.GetWidth() * info.GetHeight() * 4)
+    {
+      // This is the case of RT-DOSE (uint32_t values)
+      
+      std::auto_ptr<Orthanc::ImageAccessor> image
+        (new StringImage(Orthanc::PixelFormat_Grayscale32, info.GetWidth(),
+                         info.GetHeight(), raw));
+      
+      // TODO - Only for big endian
+      for (unsigned int y = 0; y < image->GetHeight(); y++)
+      {
+        uint32_t *p = reinterpret_cast<uint32_t*>(image->GetRow(y));
+        for (unsigned int x = 0; x < image->GetWidth(); x++, p++)
+        {
+          *p = le32toh(*p);
+        }
+      }
+      
+      NotifySliceImageSuccess(operation, *image);
+    }
+    else if (info.GetBitsAllocated() == 16 &&
+             info.GetBitsStored() == 16 &&
+             info.GetHighBit() == 15 &&
+             info.GetChannelCount() == 1 &&
+             !info.IsSigned() &&
+             info.GetPhotometricInterpretation() == Orthanc::PhotometricInterpretation_Monochrome2 &&
+             raw.size() == info.GetWidth() * info.GetHeight() * 2)
+    {
+      std::auto_ptr<Orthanc::ImageAccessor> image
+        (new StringImage(Orthanc::PixelFormat_Grayscale16, info.GetWidth(),
+                         info.GetHeight(), raw));
+      
+      // TODO - Big endian ?
+      
+      NotifySliceImageSuccess(operation, *image);
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+    }
+
+  }
+  
+  
+  OrthancSlicesLoader::OrthancSlicesLoader(OrthancStone::MessageBroker& broker,
+                                           OrthancApiClient& orthanc) :
+    OrthancStone::IObservable(broker),
+    OrthancStone::IObserver(broker),
+    orthanc_(orthanc),
+    state_(State_Initialization)
+  {
+  }
+  
+  
+  void OrthancSlicesLoader::ScheduleLoadSeries(const std::string& seriesId)
+  {
+    if (state_ != State_Initialization)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+    else
+    {
+      state_ = State_LoadingGeometry;
+      orthanc_.GetJsonAsync("/series/" + seriesId + "/instances-tags",
+                            new OrthancStone::Callable<OrthancSlicesLoader, OrthancApiClient::JsonResponseReadyMessage>(*this, &OrthancSlicesLoader::ParseSeriesGeometry),
+                            new OrthancStone::Callable<OrthancSlicesLoader, IWebService::HttpRequestErrorMessage>(*this, &OrthancSlicesLoader::OnGeometryError),
+                            NULL);
+    }
+  }
+  
+  void OrthancSlicesLoader::ScheduleLoadInstance(const std::string& instanceId)
+  {
+    if (state_ != State_Initialization)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+    else
+    {
+      state_ = State_LoadingGeometry;
+      
+      // Tag "3004-000c" is "Grid Frame Offset Vector", which is
+      // mandatory to read RT DOSE, but is too long to be returned by default
+      orthanc_.GetJsonAsync("/instances/" + instanceId + "/tags?ignore-length=3004-000c",
+                            new OrthancStone::Callable<OrthancSlicesLoader, OrthancApiClient::JsonResponseReadyMessage>(*this, &OrthancSlicesLoader::ParseInstanceGeometry),
+                            new OrthancStone::Callable<OrthancSlicesLoader, IWebService::HttpRequestErrorMessage>(*this, &OrthancSlicesLoader::OnGeometryError),
+                            Operation::DownloadInstanceGeometry(instanceId));
+    }
+  }
+  
+  
+  void OrthancSlicesLoader::ScheduleLoadFrame(const std::string& instanceId,
+                                              unsigned int frame)
+  {
+    if (state_ != State_Initialization)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+    else
+    {
+      state_ = State_LoadingGeometry;
+
+      orthanc_.GetJsonAsync("/instances/" + instanceId + "/tags",
+                            new OrthancStone::Callable<OrthancSlicesLoader, OrthancApiClient::JsonResponseReadyMessage>(*this, &OrthancSlicesLoader::ParseFrameGeometry),
+                            new OrthancStone::Callable<OrthancSlicesLoader, IWebService::HttpRequestErrorMessage>(*this, &OrthancSlicesLoader::OnGeometryError),
+                            Operation::DownloadFrameGeometry(instanceId, frame));
+    }
+  }
+  
+  
+  bool OrthancSlicesLoader::IsGeometryReady() const
+  {
+    return state_ == State_GeometryReady;
+  }
+  
+  
+  size_t OrthancSlicesLoader::GetSlicesCount() const
+  {
+    if (state_ != State_GeometryReady)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+    
+    return slices_.GetSlicesCount();
+  }
+  
+  
+  const Slice& OrthancSlicesLoader::GetSlice(size_t index) const
+  {
+    if (state_ != State_GeometryReady)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+
+    return dynamic_cast<const Slice&>(slices_.GetSlicePayload(index));
+  }
+  
+  
+  bool OrthancSlicesLoader::LookupSlice(size_t& index,
+                                        const OrthancStone::CoordinateSystem3D& plane) const
+  {
+    if (state_ != State_GeometryReady)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+
+    double distance;
+    return (slices_.LookupClosestSlice(index, distance, plane) &&
+            distance <= GetSlice(index).GetThickness() / 2.0);
+  }
+  
+  
+  void OrthancSlicesLoader::ScheduleSliceImagePng(const Slice& slice,
+                                                  size_t index)
+  {
+    std::string uri = ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" +
+                       boost::lexical_cast<std::string>(slice.GetFrame()));
+    
+    switch (slice.GetConverter().GetExpectedPixelFormat())
+    {
+      case Orthanc::PixelFormat_RGB24:
+        uri += "/preview";
+        break;
+      
+      case Orthanc::PixelFormat_Grayscale16:
+        uri += "/image-uint16";
+        break;
+      
+      case Orthanc::PixelFormat_SignedGrayscale16:
+        uri += "/image-int16";
+        break;
+      
+      default:
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+    
+    orthanc_.GetBinaryAsync(uri, "image/png",
+      new OrthancStone::Callable<OrthancSlicesLoader, 
+        OrthancApiClient::BinaryResponseReadyMessage>
+          (*this, &OrthancSlicesLoader::ParseSliceImagePng),
+      new OrthancStone::Callable<OrthancSlicesLoader, 
+        IWebService::HttpRequestErrorMessage>
+          (*this, &OrthancSlicesLoader::OnSliceImageError),
+      Operation::DownloadSliceImage(
+        static_cast<unsigned int>(index), slice, SliceImageQuality_FullPng));
+}
+  
+  void OrthancSlicesLoader::ScheduleSliceImagePam(const Slice& slice,
+                                                  size_t index)
+  {
+    std::string uri = 
+      ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" +
+      boost::lexical_cast<std::string>(slice.GetFrame()));
+
+    switch (slice.GetConverter().GetExpectedPixelFormat())
+    {
+      case Orthanc::PixelFormat_RGB24:
+        uri += "/preview";
+        break;
+
+      case Orthanc::PixelFormat_Grayscale16:
+        uri += "/image-uint16";
+        break;
+
+      case Orthanc::PixelFormat_SignedGrayscale16:
+        uri += "/image-int16";
+        break;
+
+      default:
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+
+    orthanc_.GetBinaryAsync(uri, "image/x-portable-arbitrarymap",
+      new OrthancStone::Callable<OrthancSlicesLoader, 
+        OrthancApiClient::BinaryResponseReadyMessage>
+          (*this, &OrthancSlicesLoader::ParseSliceImagePam),
+      new OrthancStone::Callable<OrthancSlicesLoader, 
+        IWebService::HttpRequestErrorMessage>
+          (*this, &OrthancSlicesLoader::OnSliceImageError),
+      Operation::DownloadSliceImage(static_cast<unsigned int>(index), 
+                                    slice, SliceImageQuality_FullPam));
+  }
+
+
+  
+  void OrthancSlicesLoader::ScheduleSliceImageJpeg(const Slice& slice,
+                                                   size_t index,
+                                                   SliceImageQuality quality)
+  {
+    unsigned int value;
+    
+    switch (quality)
+    {
+      case SliceImageQuality_Jpeg50:
+        value = 50;
+        break;
+
+      case SliceImageQuality_Jpeg90:
+        value = 90;
+        break;
+
+      case SliceImageQuality_Jpeg95:
+        value = 95;
+        break;
+      
+      default:
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+    
+    // This requires the official Web viewer plugin to be installed!
+    std::string uri = ("/web-viewer/instances/jpeg" +
+                       boost::lexical_cast<std::string>(value) +
+                       "-" + slice.GetOrthancInstanceId() + "_" +
+                       boost::lexical_cast<std::string>(slice.GetFrame()));
+
+    orthanc_.GetJsonAsync(uri,
+      new OrthancStone::Callable<OrthancSlicesLoader, 
+        OrthancApiClient::JsonResponseReadyMessage>
+          (*this, &OrthancSlicesLoader::ParseSliceImageJpeg),
+      new OrthancStone::Callable<OrthancSlicesLoader, 
+        IWebService::HttpRequestErrorMessage>
+          (*this, &OrthancSlicesLoader::OnSliceImageError),
+        Operation::DownloadSliceImage(
+          static_cast<unsigned int>(index), slice, quality));
+  }
+  
+  
+  
+  void OrthancSlicesLoader::ScheduleLoadSliceImage(size_t index,
+                                                   SliceImageQuality quality)
+  {
+    if (state_ != State_GeometryReady)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+    
+    const Slice& slice = GetSlice(index);
+    
+    if (slice.HasOrthancDecoding())
+    {
+      switch (quality)
+      {
+        case SliceImageQuality_FullPng:
+          ScheduleSliceImagePng(slice, index);
+          break;
+        case SliceImageQuality_FullPam:
+          ScheduleSliceImagePam(slice, index);
+          break;
+        default:
+          ScheduleSliceImageJpeg(slice, index, quality);
+      }
+    }
+    else
+    {
+      std::string uri = ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" +
+                         boost::lexical_cast<std::string>(slice.GetFrame()) + "/raw.gz");
+      orthanc_.GetBinaryAsync(uri, IWebService::HttpHeaders(),
+        new OrthancStone::Callable<OrthancSlicesLoader, 
+          OrthancApiClient::BinaryResponseReadyMessage>
+            (*this, &OrthancSlicesLoader::ParseSliceRawImage),
+        new OrthancStone::Callable<OrthancSlicesLoader,
+          IWebService::HttpRequestErrorMessage>
+            (*this, &OrthancSlicesLoader::OnSliceImageError),
+        Operation::DownloadSliceRawImage(
+          static_cast<unsigned int>(index), slice));
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Toolbox/OrthancSlicesLoader.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,209 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../../Messages/IObservable.h"
+#include "../../StoneEnumerations.h"
+#include "../../Toolbox/SlicesSorter.h"
+#include "IWebService.h"
+#include "OrthancApiClient.h"
+#include "Slice.h"
+
+#include <Core/Images/Image.h>
+
+
+namespace Deprecated
+{
+  class OrthancSlicesLoader : public OrthancStone::IObservable, public OrthancStone::IObserver
+  {
+  public:
+    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, SliceGeometryReadyMessage, OrthancSlicesLoader);
+    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, SliceGeometryErrorMessage, OrthancSlicesLoader);
+
+    
+    class SliceImageReadyMessage : public OrthancStone::OriginMessage<OrthancSlicesLoader>
+    {
+      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
+      
+    private:
+      unsigned int                   sliceIndex_;
+      const Slice&                   slice_;
+      const Orthanc::ImageAccessor&  image_;
+      SliceImageQuality              effectiveQuality_;
+
+    public:
+      SliceImageReadyMessage(const OrthancSlicesLoader& origin,
+                             unsigned int sliceIndex,
+                             const Slice& slice,
+                             const Orthanc::ImageAccessor& image,
+                             SliceImageQuality effectiveQuality) :
+        OriginMessage(origin),
+        sliceIndex_(sliceIndex),
+        slice_(slice),
+        image_(image),
+        effectiveQuality_(effectiveQuality)
+      {
+      }
+
+      unsigned int GetSliceIndex() const
+      {
+        return sliceIndex_;
+      }
+
+      const Slice& GetSlice() const
+      {
+        return slice_;
+      }
+
+      const Orthanc::ImageAccessor& GetImage() const
+      {
+        return image_;
+      }
+
+      SliceImageQuality GetEffectiveQuality() const
+      {
+        return effectiveQuality_;
+      }        
+    };
+    
+
+    class SliceImageErrorMessage : public OrthancStone::OriginMessage<OrthancSlicesLoader>
+    {
+      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
+      
+    private:
+      const Slice&       slice_;
+      unsigned int       sliceIndex_;
+      SliceImageQuality  effectiveQuality_;
+
+    public:
+      SliceImageErrorMessage(const OrthancSlicesLoader& origin,
+                             unsigned int sliceIndex,
+                             const Slice& slice,
+                             SliceImageQuality effectiveQuality) :
+        OriginMessage(origin),
+        slice_(slice),
+        sliceIndex_(sliceIndex),
+        effectiveQuality_(effectiveQuality)
+      {
+      }
+      unsigned int GetSliceIndex() const
+      {
+        return sliceIndex_;
+      }
+
+      const Slice& GetSlice() const
+      {
+        return slice_;
+      }
+
+      SliceImageQuality GetEffectiveQuality() const
+      {
+        return effectiveQuality_;
+      }        
+    };
+    
+  private:
+    enum State
+    {
+      State_Error,
+      State_Initialization,
+      State_LoadingGeometry,
+      State_GeometryReady
+    };
+    
+    enum Mode
+    {
+      Mode_SeriesGeometry,
+      Mode_InstanceGeometry,
+      Mode_FrameGeometry,
+      Mode_LoadImage,
+      Mode_LoadRawImage,
+      Mode_LoadDicomFile
+    };
+
+    class Operation;
+
+    OrthancApiClient&  orthanc_;
+    State         state_;
+    OrthancStone::SlicesSorter  slices_;
+
+    void NotifySliceImageSuccess(const Operation& operation,
+                                 const Orthanc::ImageAccessor& image);
+    
+    void NotifySliceImageError(const Operation& operation);
+
+    void OnGeometryError(const IWebService::HttpRequestErrorMessage& message);
+
+    void OnSliceImageError(const IWebService::HttpRequestErrorMessage& message);
+
+    void ParseSeriesGeometry(const OrthancApiClient::JsonResponseReadyMessage& message);
+
+    void ParseInstanceGeometry(const OrthancApiClient::JsonResponseReadyMessage& message);
+
+    void ParseFrameGeometry(const OrthancApiClient::JsonResponseReadyMessage& message);
+
+    void ParseSliceImagePng(const OrthancApiClient::BinaryResponseReadyMessage& message);
+
+    void ParseSliceImagePam(const OrthancApiClient::BinaryResponseReadyMessage& message);
+
+    void ParseSliceImageJpeg(const OrthancApiClient::JsonResponseReadyMessage& message);
+
+    void ParseSliceRawImage(const OrthancApiClient::BinaryResponseReadyMessage& message);
+
+    void ScheduleSliceImagePng(const Slice& slice,
+                               size_t index);
+
+    void ScheduleSliceImagePam(const Slice& slice,
+                               size_t index);
+
+    void ScheduleSliceImageJpeg(const Slice& slice,
+                                size_t index,
+                                SliceImageQuality quality);
+
+    void SortAndFinalizeSlices();
+    
+  public:
+    OrthancSlicesLoader(OrthancStone::MessageBroker& broker,
+                        //ISliceLoaderObserver& callback,
+                        OrthancApiClient& orthancApi);
+
+    void ScheduleLoadSeries(const std::string& seriesId);
+
+    void ScheduleLoadInstance(const std::string& instanceId);
+
+    void ScheduleLoadFrame(const std::string& instanceId,
+                           unsigned int frame);
+
+    bool IsGeometryReady() const;
+
+    size_t GetSlicesCount() const;
+
+    const Slice& GetSlice(size_t index) const;
+
+    bool LookupSlice(size_t& index,
+                     const OrthancStone::CoordinateSystem3D& plane) const;
+
+    void ScheduleLoadSliceImage(size_t index,
+                                SliceImageQuality requestedQuality);
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Toolbox/Slice.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,367 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "Slice.h"
+
+#include "../../StoneEnumerations.h"
+#include "../../Toolbox/GeometryToolbox.h"
+
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+#include <Core/Toolbox.h>
+
+#include <boost/lexical_cast.hpp>
+
+namespace Deprecated
+{
+  static bool ParseDouble(double& target,
+                          const std::string& source)
+  {
+    try
+    {
+      target = boost::lexical_cast<double>(source);
+      return true;
+    }
+    catch (boost::bad_lexical_cast&)
+    {
+      return false;
+    }
+  }
+
+  Slice* Slice::Clone() const
+  {
+    std::auto_ptr<Slice> target(new Slice());
+
+    target->type_ = type_;
+    target->orthancInstanceId_ = orthancInstanceId_;
+    target->sopClassUid_ = sopClassUid_;
+    target->frame_ = frame_;
+    target->frameCount_ = frameCount_;
+    target->geometry_ = geometry_;
+    target->pixelSpacingX_ = pixelSpacingX_;
+    target->pixelSpacingY_ = pixelSpacingY_;
+    target->thickness_ = thickness_;
+    target->width_ = width_;
+    target->height_ = height_;
+    target->converter_ = converter_;
+    if (imageInformation_.get() != NULL)
+      target->imageInformation_.reset(imageInformation_->Clone());
+
+    return target.release();
+  }
+  
+  bool Slice::ComputeRTDoseGeometry(const Orthanc::DicomMap& dataset,
+                                    unsigned int frame)
+  {
+    // http://dicom.nema.org/medical/Dicom/2016a/output/chtml/part03/sect_C.8.8.3.2.html
+
+    {
+      std::string increment;
+
+      if (dataset.CopyToString(increment, Orthanc::DICOM_TAG_FRAME_INCREMENT_POINTER, false))
+      {
+        Orthanc::Toolbox::ToUpperCase(increment);
+        if (increment != "3004,000C")  // This is the "Grid Frame Offset Vector" tag
+        {
+          LOG(ERROR) << "Bad value for the \"FrameIncrementPointer\" tag";
+          return false;
+        }
+      }
+    }
+
+    std::string offsetTag;
+
+    if (!dataset.CopyToString(offsetTag, Orthanc::DICOM_TAG_GRID_FRAME_OFFSET_VECTOR, false) ||
+        offsetTag.empty())
+    {
+      LOG(ERROR) << "Cannot read the \"GridFrameOffsetVector\" tag, check you are using Orthanc >= 1.3.1";
+      return false;
+    }
+
+    std::vector<std::string> offsets;
+    Orthanc::Toolbox::TokenizeString(offsets, offsetTag, '\\');
+
+    if (frameCount_ <= 1 ||
+        offsets.size() < frameCount_ ||
+        offsets.size() < 2 ||
+        frame >= frameCount_)
+    {
+      LOG(ERROR) << "No information about the 3D location of some slice(s) in a RT DOSE";
+      return false;
+    }
+
+    double offset0, offset1, z;
+
+    if (!ParseDouble(offset0, offsets[0]) ||
+        !ParseDouble(offset1, offsets[1]) ||
+        !ParseDouble(z, offsets[frame]))
+    {
+      LOG(ERROR) << "Invalid syntax";
+      return false;
+    }
+
+    if (!OrthancStone::LinearAlgebra::IsCloseToZero(offset0))
+    {
+      LOG(ERROR) << "Invalid syntax";
+      return false;
+    }
+
+    geometry_ = OrthancStone::CoordinateSystem3D(geometry_.GetOrigin() + z * geometry_.GetNormal(),
+                                                 //+ 650 * geometry_.GetAxisX(),
+                                                 geometry_.GetAxisX(),
+                                                 geometry_.GetAxisY());
+
+    thickness_ = offset1 - offset0;
+    if (thickness_ < 0)
+    {
+      thickness_ = -thickness_;
+    }
+
+    return true;
+  }
+
+  
+  bool Slice::ParseOrthancFrame(const Orthanc::DicomMap& dataset,
+                                const std::string& instanceId,
+                                unsigned int frame)
+  {
+    orthancInstanceId_ = instanceId;
+    frame_ = frame;
+    type_ = Type_OrthancDecodableFrame;
+    imageInformation_.reset(new Orthanc::DicomImageInformation(dataset));
+
+    if (!dataset.CopyToString(sopClassUid_, Orthanc::DICOM_TAG_SOP_CLASS_UID, false) ||
+        sopClassUid_.empty())
+    {
+      LOG(ERROR) << "Instance without a SOP class UID";
+      return false; 
+    }
+
+    if (!dataset.ParseUnsignedInteger32(frameCount_, Orthanc::DICOM_TAG_NUMBER_OF_FRAMES))
+    {
+      frameCount_ = 1;   // Assume instance with one frame
+    }
+
+    if (frame >= frameCount_)
+    {
+      return false;
+    }
+
+    if (!dataset.ParseUnsignedInteger32(width_, Orthanc::DICOM_TAG_COLUMNS) ||
+        !dataset.ParseUnsignedInteger32(height_, Orthanc::DICOM_TAG_ROWS))
+    {
+      return false;
+    }
+
+    thickness_ = 100.0 * std::numeric_limits<double>::epsilon();
+
+    std::string tmp;
+    if (dataset.CopyToString(tmp, Orthanc::DICOM_TAG_SLICE_THICKNESS, false))
+    {
+      if (!tmp.empty() &&
+          !ParseDouble(thickness_, tmp))
+      {
+        return false;  // Syntax error
+      }
+    }
+    
+    converter_.ReadParameters(dataset);
+
+    OrthancStone::GeometryToolbox::GetPixelSpacing(pixelSpacingX_, pixelSpacingY_, dataset);
+
+    std::string position, orientation;
+    if (dataset.CopyToString(position, Orthanc::DICOM_TAG_IMAGE_POSITION_PATIENT, false) &&
+        dataset.CopyToString(orientation, Orthanc::DICOM_TAG_IMAGE_ORIENTATION_PATIENT, false))
+    {
+      geometry_ = OrthancStone::CoordinateSystem3D(position, orientation);
+
+      bool ok = true;
+
+      switch (OrthancStone::StringToSopClassUid(sopClassUid_))
+      {
+        case OrthancStone::SopClassUid_RTDose:
+          type_ = Type_OrthancRawFrame;
+          ok = ComputeRTDoseGeometry(dataset, frame);
+          break;
+            
+        default:
+          break;
+      }
+
+      if (!ok)
+      {
+        LOG(ERROR) << "Cannot deduce the 3D location of frame " << frame
+                   << " in instance " << instanceId << ", whose SOP class UID is: " << sopClassUid_;
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  
+  const std::string Slice::GetOrthancInstanceId() const
+  {
+    if (type_ == Type_OrthancDecodableFrame ||
+        type_ == Type_OrthancRawFrame)
+    {
+      return orthancInstanceId_;
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }   
+  }
+
+  
+  unsigned int Slice::GetFrame() const
+  {
+    if (type_ == Type_Invalid)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+        
+    return frame_;
+  }
+
+  
+  const OrthancStone::CoordinateSystem3D& Slice::GetGeometry() const
+  {
+    if (type_ == Type_Invalid)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+        
+    return geometry_;
+  }
+
+  
+  double Slice::GetThickness() const
+  {
+    if (type_ == Type_Invalid)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+        
+    return thickness_;
+  }
+
+  
+  double Slice::GetPixelSpacingX() const
+  {
+    if (type_ == Type_Invalid)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+        
+    return pixelSpacingX_;
+  }
+
+  
+  double Slice::GetPixelSpacingY() const
+  {
+    if (type_ == Type_Invalid)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+        
+    return pixelSpacingY_;
+  }
+
+  
+  unsigned int Slice::GetWidth() const
+  {
+    if (type_ == Type_Invalid)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+        
+    return width_;
+  }
+
+  
+  unsigned int Slice::GetHeight() const
+  {
+    if (type_ == Type_Invalid)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+        
+    return height_;
+  }
+
+
+  const DicomFrameConverter& Slice::GetConverter() const
+  {
+    if (type_ == Type_Invalid)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+        
+    return converter_;
+  }
+
+
+  bool Slice::ContainsPlane(const OrthancStone::CoordinateSystem3D& plane) const
+  {
+    if (type_ == Type_Invalid)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+
+    bool opposite;
+    return (OrthancStone::GeometryToolbox::IsParallelOrOpposite(opposite,
+                                                                GetGeometry().GetNormal(),
+                                                                plane.GetNormal()) &&
+            OrthancStone::LinearAlgebra::IsNear(GetGeometry().ProjectAlongNormal(GetGeometry().GetOrigin()),
+                                                GetGeometry().ProjectAlongNormal(plane.GetOrigin()),
+                                                thickness_ / 2.0));
+  }
+
+  
+  void Slice::GetExtent(std::vector<OrthancStone::Vector>& points) const
+  {
+    double sx = GetPixelSpacingX();
+    double sy = GetPixelSpacingY();
+    double w = static_cast<double>(GetWidth());
+    double h = static_cast<double>(GetHeight());
+
+    points.clear();
+    points.push_back(GetGeometry().MapSliceToWorldCoordinates(-0.5      * sx, -0.5      * sy));
+    points.push_back(GetGeometry().MapSliceToWorldCoordinates((w - 0.5) * sx, -0.5      * sy));
+    points.push_back(GetGeometry().MapSliceToWorldCoordinates(-0.5      * sx, (h - 0.5) * sy));
+    points.push_back(GetGeometry().MapSliceToWorldCoordinates((w - 0.5) * sx, (h - 0.5) * sy));
+  }
+
+
+  const Orthanc::DicomImageInformation& Slice::GetImageInformation() const
+  {
+    if (imageInformation_.get() == NULL)
+    {
+      // Only available if constructing the "Slice" object with a DICOM map
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+    else
+    {
+      return *imageInformation_;
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Toolbox/Slice.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,155 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../../Toolbox/CoordinateSystem3D.h"
+#include "DicomFrameConverter.h"
+
+#include <Core/DicomFormat/DicomImageInformation.h>
+#include <Core/IDynamicObject.h>
+
+namespace Deprecated
+{
+  // TODO - Remove this class
+  class Slice :
+    public Orthanc::IDynamicObject  /* to be used as a payload of SlicesSorter */
+  {
+  private:
+    enum Type
+    {
+      Type_Invalid,
+      Type_Standalone,
+      Type_OrthancDecodableFrame,
+      Type_OrthancRawFrame
+      // TODO A slice could come from some DICOM file (URL)
+    };
+
+    bool ComputeRTDoseGeometry(const Orthanc::DicomMap& dataset,
+                               unsigned int frame);
+
+    Type                 type_;
+    std::string          orthancInstanceId_;
+    std::string          sopClassUid_;
+    unsigned int         frame_;
+    unsigned int         frameCount_;   // TODO : Redundant with "imageInformation_"
+    OrthancStone::CoordinateSystem3D   geometry_;
+    double               pixelSpacingX_;
+    double               pixelSpacingY_;
+    double               thickness_;
+    unsigned int         width_;   // TODO : Redundant with "imageInformation_"
+    unsigned int         height_;   // TODO : Redundant with "imageInformation_"
+    DicomFrameConverter  converter_;   // TODO : Partially redundant with "imageInformation_"
+
+    std::auto_ptr<Orthanc::DicomImageInformation>  imageInformation_;
+
+  public:
+    Slice() :
+      type_(Type_Invalid)
+    {
+    }
+
+
+    // this constructor is used to reference, i.e, a slice that is being loaded
+    Slice(const std::string& orthancInstanceId,
+          unsigned int frame) :
+      type_(Type_Invalid),
+      orthancInstanceId_(orthancInstanceId),
+      frame_(frame)
+    {        
+    }
+
+    // TODO Is this constructor the best way to go to tackle missing
+    // layers within SliceViewerWidget?
+    Slice(const OrthancStone::CoordinateSystem3D& plane,
+          double thickness) :
+      type_(Type_Standalone),
+      frame_(0),
+      frameCount_(0),
+      geometry_(plane),
+      pixelSpacingX_(1),
+      pixelSpacingY_(1),
+      thickness_(thickness),
+      width_(0),
+      height_(0)
+    {      
+    }
+
+    Slice(const OrthancStone::CoordinateSystem3D& plane,
+          double pixelSpacingX,
+          double pixelSpacingY,
+          double thickness,
+          unsigned int width,
+          unsigned int height,
+          const DicomFrameConverter& converter) :
+      type_(Type_Standalone),
+      frameCount_(1),
+      geometry_(plane),
+      pixelSpacingX_(pixelSpacingX),
+      pixelSpacingY_(pixelSpacingY),
+      thickness_(thickness),
+      width_(width),
+      height_(height),
+      converter_(converter)
+    {
+    }
+
+    bool IsValid() const
+    {
+      return type_ != Type_Invalid;
+    } 
+
+    bool ParseOrthancFrame(const Orthanc::DicomMap& dataset,
+                           const std::string& instanceId,
+                           unsigned int frame);
+
+    bool HasOrthancDecoding() const
+    {
+      return type_ == Type_OrthancDecodableFrame;
+    }
+
+    const std::string GetOrthancInstanceId() const;
+
+    unsigned int GetFrame() const;
+
+    const OrthancStone::CoordinateSystem3D& GetGeometry() const;
+
+    double GetThickness() const;
+
+    double GetPixelSpacingX() const;
+
+    double GetPixelSpacingY() const;
+
+    unsigned int GetWidth() const;
+
+    unsigned int GetHeight() const;
+
+    const DicomFrameConverter& GetConverter() const;
+
+    bool ContainsPlane(const OrthancStone::CoordinateSystem3D& plane) const;
+
+    void GetExtent(std::vector<OrthancStone::Vector>& points) const;
+
+    const Orthanc::DicomImageInformation& GetImageInformation() const;
+
+    Slice* Clone() const;
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Toolbox/ViewportGeometry.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,217 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "ViewportGeometry.h"
+
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+
+#include <boost/math/special_functions/round.hpp>
+
+namespace Deprecated
+{
+  void ViewportGeometry::ComputeTransform()
+  {
+    // The following lines must be read in reverse order!
+    cairo_matrix_t tmp;
+    
+    // Bring the center of the scene to the center of the view
+    cairo_matrix_init_translate(&transform_, 
+                                panX_ + static_cast<double>(width_) / 2.0, 
+                                panY_ + static_cast<double>(height_) / 2.0);
+
+    // Apply the zoom around (0,0)
+    cairo_matrix_init_scale(&tmp, zoom_, zoom_);
+    cairo_matrix_multiply(&transform_, &tmp, &transform_);
+
+    // Bring the center of the scene to (0,0)
+    cairo_matrix_init_translate(&tmp,
+                                -(sceneExtent_.GetX1() + sceneExtent_.GetX2()) / 2.0,
+                                -(sceneExtent_.GetY1() + sceneExtent_.GetY2()) / 2.0);
+    cairo_matrix_multiply(&transform_, &tmp, &transform_);
+  }
+
+
+  ViewportGeometry::ViewportGeometry()
+  {
+    width_ = 0;
+    height_ = 0;
+
+    zoom_ = 1;
+    panX_ = 0;
+    panY_ = 0;
+
+    ComputeTransform();
+  }
+
+
+  void ViewportGeometry::SetDisplaySize(unsigned int width,
+                                        unsigned int height)
+  {
+    if (width_ != width ||
+        height_ != height)
+    {
+      LOG(INFO) << "New display size: " << width << "x" << height;
+
+      width_ = width;
+      height_ = height;
+
+      ComputeTransform();
+    }
+  }
+
+
+  void ViewportGeometry::SetSceneExtent(const OrthancStone::Extent2D& extent)
+  {
+    LOG(INFO) << "New scene extent: ("
+              << extent.GetX1() << "," << extent.GetY1() << ") => ("
+              << extent.GetX2() << "," << extent.GetY2() << ")";
+
+    sceneExtent_ = extent;
+    ComputeTransform();
+  }
+
+
+  void ViewportGeometry::MapDisplayToScene(double& sceneX /* out */,
+                                           double& sceneY /* out */,
+                                           double x,
+                                           double y) const
+  {
+    cairo_matrix_t transform = transform_;
+
+    if (cairo_matrix_invert(&transform) != CAIRO_STATUS_SUCCESS)
+    {
+      LOG(ERROR) << "Cannot invert singular matrix";
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+    }
+
+    sceneX = x;
+    sceneY = y;
+    cairo_matrix_transform_point(&transform, &sceneX, &sceneY);
+  }
+
+
+  void ViewportGeometry::MapSceneToDisplay(int& displayX /* out */,
+                                           int& displayY /* out */,
+                                           double x,
+                                           double y) const
+  {
+    cairo_matrix_transform_point(&transform_, &x, &y);
+
+    displayX = static_cast<int>(boost::math::iround(x));
+    displayY = static_cast<int>(boost::math::iround(y));
+  }
+
+
+  void ViewportGeometry::MapPixelCenterToScene(std::vector<Touch>& sceneTouches /* out */,
+                                               const std::vector<Touch>& displayTouches) const
+  {
+    double sceneX, sceneY;
+    sceneTouches.clear();
+    for (size_t t = 0; t < displayTouches.size(); t++)
+    {
+      MapPixelCenterToScene(
+        sceneX,
+        sceneY, 
+        static_cast<int>(displayTouches[t].x), 
+        static_cast<int>(displayTouches[t].y));
+      
+      sceneTouches.push_back(Touch((float)sceneX, (float)sceneY));
+    }
+  }
+
+  void ViewportGeometry::MapPixelCenterToScene(double& sceneX,
+                                               double& sceneY,
+                                               int x,
+                                               int y) const
+  {
+    // Take the center of the pixel
+    MapDisplayToScene(sceneX, sceneY,
+                      static_cast<double>(x) + 0.5,
+                      static_cast<double>(y) + 0.5);
+  }
+
+
+  void ViewportGeometry::FitContent()
+  {
+    if (width_ > 0 &&
+        height_ > 0 &&
+        !sceneExtent_.IsEmpty())
+    {
+      double zoomX = static_cast<double>(width_) / (sceneExtent_.GetX2() - sceneExtent_.GetX1());
+      double zoomY = static_cast<double>(height_) / (sceneExtent_.GetY2() - sceneExtent_.GetY1());
+      zoom_ = zoomX < zoomY ? zoomX : zoomY;
+
+      panX_ = 0;
+      panY_ = 0;
+
+      ComputeTransform();
+    }
+  }
+
+
+  void ViewportGeometry::ApplyTransform(OrthancStone::CairoContext& context) const
+  {
+    cairo_set_matrix(context.GetObject(), &transform_);
+  }
+
+
+  void ViewportGeometry::GetPan(double& x,
+                                double& y) const
+  {
+    x = panX_;
+    y = panY_;
+  }
+
+
+  void ViewportGeometry::SetPan(double x,
+                                double y)
+  {
+    panX_ = x;
+    panY_ = y;
+    ComputeTransform();
+  }
+
+
+  void ViewportGeometry::SetZoom(double zoom)
+  {
+    zoom_ = zoom;
+    ComputeTransform();
+  }
+
+
+  OrthancStone::Matrix ViewportGeometry::GetMatrix() const
+  {
+    OrthancStone::Matrix m(3, 3);
+
+    m(0, 0) = transform_.xx;
+    m(0, 1) = transform_.xy;
+    m(0, 2) = transform_.x0;
+    m(1, 0) = transform_.yx;
+    m(1, 1) = transform_.yy;
+    m(1, 2) = transform_.y0;
+    m(2, 0) = 0;
+    m(2, 1) = 0;
+    m(2, 2) = 1;
+    
+    return m;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Toolbox/ViewportGeometry.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,110 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../../Viewport/CairoContext.h"
+#include "../../Toolbox/Extent2D.h"
+#include "../../Toolbox/LinearAlgebra.h"
+#include "../Viewport/IMouseTracker.h"  // to include "Touch" definition
+
+namespace Deprecated
+{
+  class ViewportGeometry
+  {
+  private:
+    // Extent of the scene (in world units)
+    OrthancStone::Extent2D   sceneExtent_;
+
+    // Size of the display (in pixels)
+    unsigned int  width_;
+    unsigned int  height_;
+
+    // Zoom/pan
+    double   zoom_;
+    double   panX_;  // In pixels (display units)
+    double   panY_;
+
+    cairo_matrix_t  transform_;  // Scene-to-display transformation
+
+    void ComputeTransform();
+
+  public:
+    ViewportGeometry();
+
+    void SetDisplaySize(unsigned int width,
+                        unsigned int height);
+
+    void SetSceneExtent(const OrthancStone::Extent2D& extent);
+
+    const OrthancStone::Extent2D& GetSceneExtent() const
+    {
+      return sceneExtent_;
+    }
+
+    void MapDisplayToScene(double& sceneX /* out */,
+                           double& sceneY /* out */,
+                           double x,
+                           double y) const;
+
+    void MapPixelCenterToScene(double& sceneX /* out */,
+                               double& sceneY /* out */,
+                               int x,
+                               int y) const;
+
+    void MapPixelCenterToScene(std::vector<Touch>& sceneTouches /* out */,
+                               const std::vector<Touch>& displayTouches) const;
+
+    void MapSceneToDisplay(int& displayX /* out */,
+                           int& displayY /* out */,
+                           double x,
+                           double y) const;
+
+    unsigned int GetDisplayWidth() const
+    {
+      return width_;
+    }
+
+    unsigned int GetDisplayHeight() const
+    {
+      return height_;
+    }
+
+    double GetZoom() const
+    {
+      return zoom_;
+    }
+
+    void FitContent();
+
+    void ApplyTransform(OrthancStone::CairoContext& context) const;
+
+    void GetPan(double& x,
+                double& y) const;
+
+    void SetPan(double x,
+                double y);
+
+    void SetZoom(double zoom);
+
+    OrthancStone::Matrix GetMatrix() const;
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Viewport/IMouseTracker.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,66 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../../Viewport/CairoSurface.h"
+#include <vector>
+
+namespace Deprecated
+{
+  struct Touch
+  {
+    float x;
+    float y;
+
+    Touch(float x, float y)
+    : x(x),
+      y(y)
+    {
+    }
+    Touch()
+      : x(0.0f),
+        y(0.0f)
+    {
+    }
+  };
+
+
+  // this is tracking a mouse in screen coordinates/pixels unlike
+  // the IWorldSceneMouseTracker that is tracking a mouse
+  // in scene coordinates/mm.
+  class IMouseTracker : public boost::noncopyable
+  {
+  public:
+    virtual ~IMouseTracker()
+    {
+    }
+    
+    virtual void Render(Orthanc::ImageAccessor& surface) = 0;
+
+    virtual void MouseUp() = 0;
+
+    // Returns "true" iff. the background scene must be repainted
+    virtual void MouseMove(int x, 
+                           int y,
+                           const std::vector<Touch>& displayTouches) = 0;
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Viewport/IStatusBar.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,40 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include <string>
+#include <boost/noncopyable.hpp>
+
+namespace Deprecated
+{
+  class IStatusBar : public boost::noncopyable
+  {
+  public:
+    virtual ~IStatusBar()
+    {
+    }
+    
+    virtual void ClearMessage() = 0;
+
+    virtual void SetMessage(const std::string& message) = 0;
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Viewport/IViewport.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,95 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "IStatusBar.h"
+#include "../../StoneEnumerations.h"
+#include "../../Messages/IObservable.h"
+
+#include <Core/Images/ImageAccessor.h>
+#include "../Viewport/IMouseTracker.h" // only to get the "Touch" definition
+
+namespace Deprecated
+{
+  class IWidget;   // Forward declaration
+  
+  class IViewport : public OrthancStone::IObservable
+  {
+  public:
+    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, ViewportChangedMessage, IViewport);
+
+    IViewport(OrthancStone::MessageBroker& broker) :
+      IObservable(broker)
+    {
+    }
+    
+    virtual ~IViewport()
+    {
+    }
+
+    virtual void FitContent() = 0;
+
+    virtual void SetStatusBar(IStatusBar& statusBar) = 0;
+
+    virtual void SetSize(unsigned int width,
+                         unsigned int height) = 0;
+
+    // The function returns "true" iff. a new frame was rendered
+    virtual bool Render(Orthanc::ImageAccessor& surface) = 0;
+
+    virtual void MouseDown(OrthancStone::MouseButton button,
+                           int x,
+                           int y,
+                           OrthancStone::KeyboardModifiers modifiers,
+                           const std::vector<Touch>& touches) = 0;
+
+    virtual void MouseUp() = 0;
+
+    virtual void MouseMove(int x, 
+                           int y,
+                           const std::vector<Touch>& displayTouches) = 0;
+
+    virtual void MouseEnter() = 0;
+
+    virtual void MouseLeave() = 0;
+
+    virtual void MouseWheel(OrthancStone::MouseWheelDirection direction,
+                            int x,
+                            int y,
+                            OrthancStone::KeyboardModifiers modifiers) = 0;
+
+    virtual void KeyPressed(OrthancStone::KeyboardKeys key,
+                            char keyChar,
+                            OrthancStone::KeyboardModifiers modifiers) = 0;
+
+    virtual bool HasAnimation() = 0;
+
+    virtual void DoAnimation() = 0;
+
+    // Should only be called from IWidget
+    // TODO Why should this be virtual?
+    virtual void NotifyContentChanged()
+    {
+      BroadcastMessage(ViewportChangedMessage(*this));
+    }
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Viewport/WidgetViewport.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,289 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "WidgetViewport.h"
+
+#include <Core/Images/ImageProcessing.h>
+#include <Core/OrthancException.h>
+
+namespace Deprecated
+{
+  WidgetViewport::WidgetViewport(OrthancStone::MessageBroker& broker) :
+    IViewport(broker),
+    statusBar_(NULL),
+    isMouseOver_(false),
+    lastMouseX_(0),
+    lastMouseY_(0),
+    backgroundChanged_(false)
+  {
+  }
+
+
+  void WidgetViewport::FitContent()
+  {
+    if (centralWidget_.get() != NULL)
+    {
+      centralWidget_->FitContent();
+    }
+  }
+
+
+  void WidgetViewport::SetStatusBar(IStatusBar& statusBar)
+  {
+    statusBar_ = &statusBar;
+
+    if (centralWidget_.get() != NULL)
+    {
+      centralWidget_->SetStatusBar(statusBar);
+    }
+  }
+
+
+  IWidget& WidgetViewport::SetCentralWidget(IWidget* widget)
+  {
+    if (widget == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+
+    mouseTracker_.reset(NULL);
+
+    centralWidget_.reset(widget);
+    centralWidget_->SetViewport(*this);
+
+    if (statusBar_ != NULL)
+    {
+      centralWidget_->SetStatusBar(*statusBar_);
+    }
+
+    NotifyBackgroundChanged();
+
+    return *widget;
+  }
+
+
+  void WidgetViewport::NotifyBackgroundChanged()
+  {
+    backgroundChanged_ = true;
+    NotifyContentChanged();
+  }
+
+
+  void WidgetViewport::SetSize(unsigned int width,
+                               unsigned int height)
+  {
+    background_.SetSize(width, height, false /* no alpha */);
+
+    if (centralWidget_.get() != NULL)
+    {
+      centralWidget_->SetSize(width, height);
+    }
+
+    NotifyBackgroundChanged();
+  }
+
+
+  bool WidgetViewport::Render(Orthanc::ImageAccessor& surface)
+  {
+    if (centralWidget_.get() == NULL)
+    {
+      return false;
+    }
+
+    Orthanc::ImageAccessor background;
+    background_.GetWriteableAccessor(background);
+
+    if (backgroundChanged_ &&
+        !centralWidget_->Render(background))
+    {
+      return false;
+    }
+
+    if (background.GetWidth() != surface.GetWidth() ||
+        background.GetHeight() != surface.GetHeight())
+    {
+      return false;
+    }
+
+    Orthanc::ImageProcessing::Convert(surface, background);
+    
+    if (mouseTracker_.get() != NULL)
+    {
+      mouseTracker_->Render(surface);
+    }
+    else if (isMouseOver_)
+    {
+      centralWidget_->RenderMouseOver(surface, lastMouseX_, lastMouseY_);
+    }
+
+    return true;
+  }
+
+  void WidgetViewport::TouchStart(const std::vector<Touch>& displayTouches)
+  {
+    MouseDown(OrthancStone::MouseButton_Left, (int)displayTouches[0].x, (int)displayTouches[0].y, OrthancStone::KeyboardModifiers_None, displayTouches); // one touch is equivalent to a mouse tracker without left button -> set the mouse coordinates to the first touch coordinates
+  }
+      
+  void WidgetViewport::TouchMove(const std::vector<Touch>& displayTouches)
+  {
+    MouseMove((int)displayTouches[0].x, (int)displayTouches[0].y, displayTouches); // one touch is equivalent to a mouse tracker without left button -> set the mouse coordinates to the first touch coordinates
+  }
+      
+  void WidgetViewport::TouchEnd(const std::vector<Touch>& displayTouches)
+  {
+    // note: TouchEnd is not triggered when a single touch gesture ends (it is only triggered when
+    // going from 2 touches to 1 touch, ...)
+    MouseUp();
+  }
+
+  void WidgetViewport::MouseDown(OrthancStone::MouseButton button,
+                                 int x,
+                                 int y,
+                                 OrthancStone::KeyboardModifiers modifiers,
+                                 const std::vector<Touch>& displayTouches
+                                 )
+  {
+    lastMouseX_ = x;
+    lastMouseY_ = y;
+
+    if (centralWidget_.get() != NULL)
+    {
+      mouseTracker_.reset(centralWidget_->CreateMouseTracker(button, x, y, modifiers, displayTouches));
+    }
+    else
+    {
+      mouseTracker_.reset(NULL);
+    }      
+
+    NotifyContentChanged();
+  }
+
+
+  void WidgetViewport::MouseUp()
+  {
+    if (mouseTracker_.get() != NULL)
+    {
+      mouseTracker_->MouseUp();
+      mouseTracker_.reset(NULL);
+      NotifyContentChanged();
+    }
+  }
+
+
+  void WidgetViewport::MouseMove(int x, 
+                                 int y,
+                                 const std::vector<Touch>& displayTouches)
+  {
+    if (centralWidget_.get() == NULL)
+    {
+      return;
+    }
+
+    lastMouseX_ = x;
+    lastMouseY_ = y;
+
+    bool repaint = false;
+    
+    if (mouseTracker_.get() != NULL)
+    {
+      mouseTracker_->MouseMove(x, y, displayTouches);
+      repaint = true;
+    }
+    else
+    {
+      repaint = centralWidget_->HasRenderMouseOver();
+    }
+
+    if (repaint)
+    {
+      // The scene must be repainted, notify the observers
+      NotifyContentChanged();
+    }
+  }
+
+
+  void WidgetViewport::MouseEnter()
+  {
+    isMouseOver_ = true;
+    NotifyContentChanged();
+  }
+
+
+  void WidgetViewport::MouseLeave()
+  {
+    isMouseOver_ = false;
+
+    if (mouseTracker_.get() != NULL)
+    {
+      mouseTracker_->MouseUp();
+      mouseTracker_.reset(NULL);
+    }
+
+    NotifyContentChanged();
+  }
+
+
+  void WidgetViewport::MouseWheel(OrthancStone::MouseWheelDirection direction,
+                                  int x,
+                                  int y,
+                                  OrthancStone::KeyboardModifiers modifiers)
+  {
+    if (centralWidget_.get() != NULL &&
+        mouseTracker_.get() == NULL)
+    {
+      centralWidget_->MouseWheel(direction, x, y, modifiers);
+    }
+  }
+
+
+  void WidgetViewport::KeyPressed(OrthancStone::KeyboardKeys key,
+                                  char keyChar,
+                                  OrthancStone::KeyboardModifiers modifiers)
+  {
+    if (centralWidget_.get() != NULL &&
+        mouseTracker_.get() == NULL)
+    {
+      centralWidget_->KeyPressed(key, keyChar, modifiers);
+    }
+  }
+
+
+  bool WidgetViewport::HasAnimation()
+  {
+    if (centralWidget_.get() != NULL)
+    {
+      return centralWidget_->HasAnimation();
+    }
+    else
+    {
+      return false;
+    }
+  }
+   
+
+  void WidgetViewport::DoAnimation()
+  {
+    if (centralWidget_.get() != NULL)
+    {
+      centralWidget_->DoAnimation();
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Viewport/WidgetViewport.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,94 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "IViewport.h"
+#include "../Widgets/IWidget.h"
+
+#include <memory>
+
+namespace Deprecated
+{
+  class WidgetViewport : public IViewport
+  {
+  private:
+    std::auto_ptr<IWidget>        centralWidget_;
+    IStatusBar*                   statusBar_;
+    std::auto_ptr<IMouseTracker>  mouseTracker_;
+    bool                          isMouseOver_;
+    int                           lastMouseX_;
+    int                           lastMouseY_;
+    OrthancStone::CairoSurface    background_;
+    bool                          backgroundChanged_;
+
+  public:
+    WidgetViewport(OrthancStone::MessageBroker& broker);
+
+    virtual void FitContent();
+
+    virtual void SetStatusBar(IStatusBar& statusBar);
+
+    IWidget& SetCentralWidget(IWidget* widget);  // Takes ownership
+
+    virtual void NotifyBackgroundChanged();
+
+    virtual void SetSize(unsigned int width,
+                         unsigned int height);
+
+    virtual bool Render(Orthanc::ImageAccessor& surface);
+
+    virtual void MouseDown(OrthancStone::MouseButton button,
+                           int x,
+                           int y,
+                           OrthancStone::KeyboardModifiers modifiers,
+                           const std::vector<Touch>& displayTouches);
+
+    virtual void MouseUp();
+
+    virtual void MouseMove(int x, 
+                           int y,
+                           const std::vector<Touch>& displayTouches);
+
+    virtual void MouseEnter();
+
+    virtual void MouseLeave();
+
+    virtual void TouchStart(const std::vector<Touch>& touches);
+    
+    virtual void TouchMove(const std::vector<Touch>& touches);
+    
+    virtual void TouchEnd(const std::vector<Touch>& touches);
+
+    virtual void MouseWheel(OrthancStone::MouseWheelDirection direction,
+                            int x,
+                            int y,
+                            OrthancStone::KeyboardModifiers modifiers);
+
+    virtual void KeyPressed(OrthancStone::KeyboardKeys key,
+                            char keyChar,
+                            OrthancStone::KeyboardModifiers modifiers);
+
+    virtual bool HasAnimation();
+
+    virtual void DoAnimation();
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Volumes/ISlicedVolume.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,77 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../../Messages/IObservable.h"
+#include "../Toolbox/Slice.h"
+
+namespace Deprecated
+{
+  class ISlicedVolume : public OrthancStone::IObservable
+  {
+  public:
+    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, ContentChangedMessage, ISlicedVolume);
+    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryErrorMessage, ISlicedVolume);
+    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryReadyMessage, ISlicedVolume);
+    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, VolumeReadyMessage, ISlicedVolume);
+
+
+    class SliceContentChangedMessage : public OrthancStone::OriginMessage<ISlicedVolume>
+    {
+      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
+      
+    private:
+      size_t        sliceIndex_;
+      const Slice&  slice_;
+      
+    public:
+      SliceContentChangedMessage(ISlicedVolume& origin,
+                                 size_t sliceIndex,
+                                 const Slice& slice) :
+        OriginMessage(origin),
+        sliceIndex_(sliceIndex),
+        slice_(slice)
+      {
+      }
+
+      size_t GetSliceIndex() const
+      {
+        return sliceIndex_;
+      }
+
+      const Slice& GetSlice() const
+      {
+        return slice_;
+      }
+    };
+
+
+    ISlicedVolume(OrthancStone::MessageBroker& broker) :
+      IObservable(broker)
+    {
+    }
+    
+    virtual size_t GetSliceCount() const = 0;
+
+    virtual const Slice& GetSlice(size_t slice) const = 0;
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Volumes/IVolumeLoader.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,40 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../../Messages/IObservable.h"
+
+namespace Deprecated
+{
+  class IVolumeLoader : public OrthancStone::IObservable
+  {
+  public:
+    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryReadyMessage, IVolumeLoader);
+    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryErrorMessage, IVolumeLoader);
+    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, ContentChangedMessage, IVolumeLoader);
+
+    IVolumeLoader(OrthancStone::MessageBroker& broker) :
+      IObservable(broker)
+    {
+    }
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Volumes/StructureSetLoader.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,116 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "StructureSetLoader.h"
+
+#include "../../Toolbox/MessagingToolbox.h"
+
+#include <Core/OrthancException.h>
+
+namespace Deprecated
+{
+  StructureSetLoader::StructureSetLoader(OrthancStone::MessageBroker& broker,
+                                         OrthancApiClient& orthanc) :
+    IVolumeLoader(broker),
+    IObserver(broker),
+    orthanc_(orthanc)
+  {
+  }
+  
+
+  void StructureSetLoader::OnReferencedSliceLoaded(const OrthancApiClient::JsonResponseReadyMessage& message)
+  {
+    OrthancPlugins::FullOrthancDataset dataset(message.GetJson());
+
+    Orthanc::DicomMap slice;
+    OrthancStone::MessagingToolbox::ConvertDataset(slice, dataset);
+    structureSet_->AddReferencedSlice(slice);
+
+    BroadcastMessage(ContentChangedMessage(*this));
+  }
+  
+
+  void StructureSetLoader::OnStructureSetLoaded(const OrthancApiClient::JsonResponseReadyMessage& message)
+  {
+    OrthancPlugins::FullOrthancDataset dataset(message.GetJson());
+    structureSet_.reset(new OrthancStone::DicomStructureSet(dataset));
+
+    std::set<std::string> instances;
+    structureSet_->GetReferencedInstances(instances);
+
+    for (std::set<std::string>::const_iterator it = instances.begin();
+         it != instances.end(); ++it)
+    {
+      orthanc_.PostBinaryAsyncExpectJson("/tools/lookup", *it,
+                                         new OrthancStone::Callable<StructureSetLoader, OrthancApiClient::JsonResponseReadyMessage>(*this, &StructureSetLoader::OnLookupCompleted));
+    }
+
+    BroadcastMessage(GeometryReadyMessage(*this));
+  }
+
+  
+  void StructureSetLoader::OnLookupCompleted(const OrthancApiClient::JsonResponseReadyMessage& message)
+  {
+    const Json::Value& lookup = message.GetJson();
+
+    if (lookup.type() != Json::arrayValue ||
+        lookup.size() != 1 ||
+        !lookup[0].isMember("Type") ||
+        !lookup[0].isMember("Path") ||
+        lookup[0]["Type"].type() != Json::stringValue ||
+        lookup[0]["ID"].type() != Json::stringValue ||
+        lookup[0]["Type"].asString() != "Instance")
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
+    }
+
+    const std::string& instance = lookup[0]["ID"].asString();
+    orthanc_.GetJsonAsync("/instances/" + instance + "/tags",
+                          new OrthancStone::Callable<StructureSetLoader, OrthancApiClient::JsonResponseReadyMessage>(*this, &StructureSetLoader::OnReferencedSliceLoaded));
+  }
+
+  
+  void StructureSetLoader::ScheduleLoadInstance(const std::string& instance)
+  {
+    if (structureSet_.get() != NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+    else
+    {
+      orthanc_.GetJsonAsync("/instances/" + instance + "/tags?ignore-length=3006-0050",
+                            new OrthancStone::Callable<StructureSetLoader, OrthancApiClient::JsonResponseReadyMessage>(*this, &StructureSetLoader::OnStructureSetLoaded));
+    }
+  }
+
+
+  OrthancStone::DicomStructureSet& StructureSetLoader::GetStructureSet()
+  {
+    if (structureSet_.get() == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+    else
+    {
+      return *structureSet_;
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Volumes/StructureSetLoader.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,57 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../../Toolbox/DicomStructureSet.h"
+#include "../Toolbox/OrthancApiClient.h"
+#include "IVolumeLoader.h"
+
+namespace Deprecated
+{
+  class StructureSetLoader :
+    public IVolumeLoader,
+    public OrthancStone::IObserver
+  {
+  private:
+    OrthancApiClient&                 orthanc_;
+    std::auto_ptr<OrthancStone::DicomStructureSet>  structureSet_;
+
+    void OnReferencedSliceLoaded(const OrthancApiClient::JsonResponseReadyMessage& message);
+
+    void OnStructureSetLoaded(const OrthancApiClient::JsonResponseReadyMessage& message);
+
+    void OnLookupCompleted(const OrthancApiClient::JsonResponseReadyMessage& message);
+
+  public:
+    StructureSetLoader(OrthancStone::MessageBroker& broker,
+                       OrthancApiClient& orthanc);
+
+    void ScheduleLoadInstance(const std::string& instance);
+
+    bool HasStructureSet() const
+    {
+      return structureSet_.get() != NULL;
+    }
+
+    OrthancStone::DicomStructureSet& GetStructureSet();
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Widgets/CairoWidget.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,101 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "CairoWidget.h"
+
+#include <Core/Images/ImageProcessing.h>
+#include <Core/OrthancException.h>
+
+namespace Deprecated
+{
+  static bool IsAligned(const Orthanc::ImageAccessor& target)
+  {
+    // TODO
+    return true;
+  }
+
+  CairoWidget::CairoWidget(const std::string& name) :
+    WidgetBase(name)
+  {
+  }
+
+  void CairoWidget::SetSize(unsigned int width,
+                            unsigned int height)
+  {
+    surface_.SetSize(width, height, false /* no alpha */);
+  }
+  
+
+  bool CairoWidget::Render(Orthanc::ImageAccessor& target)
+  {
+    // Don't call the base class here, as
+    // "ClearBackgroundCairo()" is a faster alternative
+
+    if (IsAligned(target))
+    {
+      OrthancStone::CairoSurface surface(target, false /* no alpha */);
+      OrthancStone::CairoContext context(surface);
+      ClearBackgroundCairo(context);
+      return RenderCairo(context);
+    }
+    else
+    {
+      OrthancStone::CairoContext context(surface_);
+      ClearBackgroundCairo(context);
+
+      if (RenderCairo(context))
+      {
+        Orthanc::ImageAccessor surface;
+        surface_.GetReadOnlyAccessor(surface);
+        Orthanc::ImageProcessing::Copy(target, surface);
+        return true;
+      }
+      else
+      {
+        return false;
+      }
+    }
+  }
+
+
+  void CairoWidget::RenderMouseOver(Orthanc::ImageAccessor& target,
+                                    int x,
+                                    int y)
+  {
+    if (IsAligned(target))
+    {
+      OrthancStone::CairoSurface surface(target, false /* no alpha */);
+      OrthancStone::CairoContext context(surface);
+      RenderMouseOverCairo(context, x, y);
+    }
+    else
+    {
+      Orthanc::ImageAccessor accessor;
+      surface_.GetWriteableAccessor(accessor);
+      Orthanc::ImageProcessing::Copy(accessor, target);
+
+      OrthancStone::CairoContext context(surface_);
+      RenderMouseOverCairo(context, x, y);
+
+      Orthanc::ImageProcessing::Copy(target, accessor);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Widgets/CairoWidget.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,52 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "WidgetBase.h"
+
+namespace Deprecated
+{
+  class CairoWidget : public WidgetBase
+  {
+  private:
+    OrthancStone::CairoSurface   surface_;
+
+  protected:
+    virtual bool RenderCairo(OrthancStone::CairoContext& context) = 0;
+    
+    virtual void RenderMouseOverCairo(OrthancStone::CairoContext& context,
+                                      int x,
+                                      int y) = 0;
+    
+  public:
+    CairoWidget(const std::string& name);
+
+    virtual void SetSize(unsigned int width,
+                         unsigned int height);
+
+    virtual bool Render(Orthanc::ImageAccessor& target);
+
+    virtual void RenderMouseOver(Orthanc::ImageAccessor& target,
+                                 int x,
+                                 int y);  
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Widgets/EmptyWidget.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,41 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "EmptyWidget.h"
+
+#include <Core/Images/ImageProcessing.h>
+#include <Core/OrthancException.h>
+
+namespace Deprecated
+{
+  bool EmptyWidget::Render(Orthanc::ImageAccessor& surface)
+  {
+    // Note: This call is slow
+    Orthanc::ImageProcessing::Set(surface, red_, green_, blue_, 255);
+    return true;
+  }
+
+
+  void EmptyWidget::DoAnimation()
+  {
+    throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Widgets/EmptyWidget.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,116 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "IWidget.h"
+
+namespace Deprecated
+{
+  /**
+   * This is a test widget that simply fills its surface with an
+   * uniform color.
+   **/
+  class EmptyWidget : public IWidget
+  {
+  private:
+    uint8_t  red_;
+    uint8_t  green_;
+    uint8_t  blue_;
+
+  public:
+    EmptyWidget(uint8_t red,
+                uint8_t green,
+                uint8_t blue) :
+      red_(red),
+      green_(green),
+      blue_(blue)
+    {
+    }
+
+    virtual void FitContent()
+    {
+    }
+
+    virtual void SetParent(IWidget& widget)
+    {
+    }
+
+    virtual void SetViewport(WidgetViewport& viewport)
+    {
+    }
+
+    virtual void NotifyContentChanged()
+    {
+    }
+
+    virtual void SetStatusBar(IStatusBar& statusBar)
+    {
+    }
+
+    virtual void SetSize(unsigned int width,
+                         unsigned int height)
+    {
+    }
+
+    virtual bool Render(Orthanc::ImageAccessor& surface);
+
+    virtual IMouseTracker* CreateMouseTracker(OrthancStone::MouseButton button,
+                                              int x,
+                                              int y,
+                                              OrthancStone::KeyboardModifiers modifiers,
+                                              const std::vector<Touch>& touches)
+    {
+      return NULL;
+    }
+
+    virtual void RenderMouseOver(Orthanc::ImageAccessor& target,
+                                 int x,
+                                 int y)
+    {
+    }
+
+    virtual void MouseWheel(OrthancStone::MouseWheelDirection direction,
+                            int x,
+                            int y,
+                            OrthancStone::KeyboardModifiers modifiers)
+    {
+    }
+
+    virtual void KeyPressed(OrthancStone::KeyboardKeys key,
+                            char keyChar,
+                            OrthancStone::KeyboardModifiers modifiers)
+    {
+    }
+
+    virtual bool HasAnimation() const
+    {
+      return false;
+    }
+
+    virtual void DoAnimation();
+
+    virtual bool HasRenderMouseOver()
+    {
+      return false;
+    }
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Widgets/IWidget.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,81 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../../StoneEnumerations.h"
+#include "../Viewport/IMouseTracker.h"
+#include "../Viewport/IStatusBar.h"
+
+namespace Deprecated
+{
+  class WidgetViewport;  // Forward declaration
+  
+  class IWidget : public boost::noncopyable
+  {
+  public:
+    virtual ~IWidget()
+    {
+    }
+
+    virtual void FitContent() = 0;
+
+    virtual void SetParent(IWidget& parent) = 0;
+    
+    virtual void SetViewport(WidgetViewport& viewport) = 0;
+
+    virtual void SetStatusBar(IStatusBar& statusBar) = 0;
+
+    virtual void SetSize(unsigned int width, 
+                         unsigned int height) = 0;
+ 
+    virtual bool Render(Orthanc::ImageAccessor& surface) = 0;
+
+    virtual IMouseTracker* CreateMouseTracker(OrthancStone::MouseButton button,
+                                              int x,
+                                              int y,
+                                              OrthancStone::KeyboardModifiers modifiers,
+                                              const std::vector<Touch>& touches) = 0;
+
+    virtual void RenderMouseOver(Orthanc::ImageAccessor& target,
+                                 int x,
+                                 int y) = 0;
+
+    virtual bool HasRenderMouseOver() = 0;
+
+    virtual void MouseWheel(OrthancStone::MouseWheelDirection direction,
+                            int x,
+                            int y,
+                            OrthancStone::KeyboardModifiers modifiers) = 0;
+
+    virtual void KeyPressed(OrthancStone::KeyboardKeys key,
+                            char keyChar,
+                            OrthancStone::KeyboardModifiers modifiers) = 0;
+
+    virtual bool HasAnimation() const = 0;
+
+    virtual void DoAnimation() = 0;
+
+    // Subclasses can call this method to signal the display of the
+    // widget must be refreshed
+    virtual void NotifyContentChanged() = 0;
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Widgets/IWorldSceneInteractor.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,70 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "IWorldSceneMouseTracker.h"
+
+#include "../Toolbox/ViewportGeometry.h"
+#include "../../StoneEnumerations.h"
+#include "../Viewport/IStatusBar.h"
+
+namespace Deprecated
+{
+    class WorldSceneWidget;
+
+    class IWorldSceneInteractor : public boost::noncopyable
+    {
+    public:
+        virtual ~IWorldSceneInteractor()
+        {
+        }
+
+        virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget,
+                                                            const ViewportGeometry& view,
+                                                            OrthancStone::MouseButton button,
+                                                            OrthancStone::KeyboardModifiers modifiers,
+                                                            int viewportX,
+                                                            int viewportY,
+                                                            double x,
+                                                            double y,
+                                                            IStatusBar* statusBar,
+                                                            const std::vector<Touch>& touches) = 0;
+
+        virtual void MouseOver(OrthancStone::CairoContext& context,
+                               WorldSceneWidget& widget,
+                               const ViewportGeometry& view,
+                               double x,
+                               double y,
+                               IStatusBar* statusBar) = 0;
+
+        virtual void MouseWheel(WorldSceneWidget& widget,
+                                OrthancStone::MouseWheelDirection direction,
+                                OrthancStone::KeyboardModifiers modifiers,
+                                IStatusBar* statusBar) = 0;
+
+        virtual void KeyPressed(WorldSceneWidget& widget,
+                                OrthancStone::KeyboardKeys key,
+                                char keyChar,
+                                OrthancStone::KeyboardModifiers modifiers,
+                                IStatusBar* statusBar) = 0;
+    };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Widgets/IWorldSceneMouseTracker.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,54 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../../Viewport/CairoContext.h"
+#include "../Viewport/IMouseTracker.h" // only to get the "Touch" definition
+
+namespace Deprecated
+{
+
+  // this is tracking a mouse in scene coordinates/mm unlike
+  // the IMouseTracker that is tracking a mouse
+  // in screen coordinates/pixels.
+  class IWorldSceneMouseTracker : public boost::noncopyable
+  {
+  public:
+    virtual ~IWorldSceneMouseTracker()
+    {
+    }
+
+    virtual bool HasRender() const = 0;
+
+    virtual void Render(OrthancStone::CairoContext& context,
+                        double zoom) = 0;
+
+    virtual void MouseUp() = 0;
+
+    virtual void MouseMove(int displayX,
+                           int displayY,
+                           double sceneX,
+                           double sceneY,
+                           const std::vector<Touch>& displayTouches,
+                           const std::vector<Touch>& sceneTouches) = 0;
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Widgets/LayoutWidget.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,503 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "LayoutWidget.h"
+
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+
+#include <boost/math/special_functions/round.hpp>
+
+namespace Deprecated
+{
+  class LayoutWidget::LayoutMouseTracker : public IMouseTracker
+  {
+  private:
+    std::auto_ptr<IMouseTracker>   tracker_;
+    int                            left_;
+    int                            top_;
+    unsigned int                   width_;
+    unsigned int                   height_;
+
+  public:
+    LayoutMouseTracker(IMouseTracker* tracker,
+                       int left,
+                       int top,
+                       unsigned int width,
+                       unsigned int height) :
+      tracker_(tracker),
+      left_(left),
+      top_(top),
+      width_(width),
+      height_(height)
+    {
+      if (tracker == NULL)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      }
+    }
+
+    virtual void Render(Orthanc::ImageAccessor& surface)
+    {
+      Orthanc::ImageAccessor accessor;
+      surface.GetRegion(accessor, left_, top_, width_, height_);
+      tracker_->Render(accessor);
+    }
+
+    virtual void MouseUp()
+    {
+      tracker_->MouseUp();
+    }
+
+    virtual void MouseMove(int x, 
+                           int y,
+                           const std::vector<Touch>& displayTouches)
+    {
+      std::vector<Touch> relativeTouches;
+      for (size_t t = 0; t < displayTouches.size(); t++)
+      {
+        relativeTouches.push_back(Touch(displayTouches[t].x - left_, displayTouches[t].y - top_));
+      }
+
+      tracker_->MouseMove(x - left_, y - top_, relativeTouches);
+    }
+  };
+
+
+  class LayoutWidget::ChildWidget : public boost::noncopyable
+  {
+  private:
+    std::auto_ptr<IWidget>  widget_;
+    int                     left_;
+    int                     top_;
+    unsigned int            width_;
+    unsigned int            height_;
+
+  public:
+    ChildWidget(IWidget* widget) :
+      widget_(widget)
+    {
+      assert(widget != NULL);
+      SetEmpty();
+    }
+
+    void DoAnimation()
+    {
+      if (widget_->HasAnimation())
+      {
+        widget_->DoAnimation();
+      }
+    }
+
+    IWidget& GetWidget() const
+    {
+      return *widget_;
+    }
+
+    void SetRectangle(unsigned int left, 
+                      unsigned int top,
+                      unsigned int width,
+                      unsigned int height)
+    {
+      left_ = left;
+      top_ = top;
+      width_ = width;
+      height_ = height;
+
+      widget_->SetSize(width, height);
+    }
+
+    void SetEmpty()
+    {
+      SetRectangle(0, 0, 0, 0);
+    }
+
+    bool Contains(int x, 
+                  int y) const
+    {
+      return (x >= left_ && 
+              y >= top_ &&
+              x < left_ + static_cast<int>(width_) &&
+              y < top_ + static_cast<int>(height_));
+    }      
+
+    bool Render(Orthanc::ImageAccessor& target)
+    {
+      if (width_ == 0 ||
+          height_ == 0)
+      {
+        return true;
+      }
+      else 
+      {
+        Orthanc::ImageAccessor accessor;
+        target.GetRegion(accessor, left_, top_, width_, height_);
+        return widget_->Render(accessor);
+      }
+    }
+
+    IMouseTracker* CreateMouseTracker(OrthancStone::MouseButton button,
+                                      int x,
+                                      int y,
+                                      OrthancStone::KeyboardModifiers modifiers,
+                                      const std::vector<Touch>& touches)
+    {
+      if (Contains(x, y))
+      {
+        IMouseTracker* tracker = widget_->CreateMouseTracker(button, 
+                                                             x - left_, 
+                                                             y - top_, 
+                                                             modifiers,
+                                                             touches);
+        if (tracker)
+        {
+          return new LayoutMouseTracker(tracker, left_, top_, width_, height_);
+        }
+      }
+
+      return NULL;
+    }
+
+    void RenderMouseOver(Orthanc::ImageAccessor& target,
+                         int x,
+                         int y)
+    {
+      if (Contains(x, y))
+      {
+        Orthanc::ImageAccessor accessor;
+        target.GetRegion(accessor, left_, top_, width_, height_);
+
+        widget_->RenderMouseOver(accessor, x - left_, y - top_);
+      }
+    }
+
+    void MouseWheel(OrthancStone::MouseWheelDirection direction,
+                    int x,
+                    int y,
+                    OrthancStone::KeyboardModifiers modifiers)
+    {
+      if (Contains(x, y))
+      {
+        widget_->MouseWheel(direction, x - left_, y - top_, modifiers);
+      }
+    }
+    
+    bool HasRenderMouseOver()
+    {
+      return widget_->HasRenderMouseOver();
+    }
+  };
+
+
+  void LayoutWidget::ComputeChildrenExtents()
+  {
+    if (children_.size() == 0)
+    {
+      return;
+    }
+
+    float internal = static_cast<float>(paddingInternal_);
+
+    if (width_ <= paddingLeft_ + paddingRight_ ||
+        height_ <= paddingTop_ + paddingBottom_)
+    {
+      for (size_t i = 0; i < children_.size(); i++)
+      {
+        children_[i]->SetEmpty();          
+      }
+    }
+    else if (isHorizontal_)
+    {
+      unsigned int padding = paddingLeft_ + paddingRight_ + (static_cast<unsigned int>(children_.size()) - 1) * paddingInternal_;
+      float childWidth = ((static_cast<float>(width_) - static_cast<float>(padding)) / 
+                          static_cast<float>(children_.size()));
+        
+      for (size_t i = 0; i < children_.size(); i++)
+      {
+        float left = static_cast<float>(paddingLeft_) + static_cast<float>(i) * (childWidth + internal);
+        float right = left + childWidth;
+
+        if (left >= right)
+        {
+          children_[i]->SetEmpty();
+        }
+        else
+        {
+          children_[i]->SetRectangle(static_cast<unsigned int>(left), 
+                                     paddingTop_, 
+                                     boost::math::iround(right - left),
+                                     height_ - paddingTop_ - paddingBottom_);
+        }
+      }
+    }
+    else
+    {
+      unsigned int padding = paddingTop_ + paddingBottom_ + (static_cast<unsigned int>(children_.size()) - 1) * paddingInternal_;
+      float childHeight = ((static_cast<float>(height_) - static_cast<float>(padding)) / 
+                           static_cast<float>(children_.size()));
+        
+      for (size_t i = 0; i < children_.size(); i++)
+      {
+        float top = static_cast<float>(paddingTop_) + static_cast<float>(i) * (childHeight + internal);
+        float bottom = top + childHeight;
+
+        if (top >= bottom)
+        {
+          children_[i]->SetEmpty();
+        }
+        else
+        {
+          children_[i]->SetRectangle(paddingTop_, 
+                                     static_cast<unsigned int>(top), 
+                                     width_ - paddingLeft_ - paddingRight_,
+                                     boost::math::iround(bottom - top));
+        }
+      }
+    }
+
+    NotifyContentChanged(*this);
+  }
+
+
+  LayoutWidget::LayoutWidget(const std::string& name) :
+    WidgetBase(name),
+    isHorizontal_(true),
+    width_(0),
+    height_(0),
+    paddingLeft_(0),
+    paddingTop_(0),
+    paddingRight_(0),
+    paddingBottom_(0),
+    paddingInternal_(0)
+  {
+  }
+
+
+  LayoutWidget::~LayoutWidget()
+  {
+    for (size_t i = 0; i < children_.size(); i++)
+    {
+      delete children_[i];
+    }
+  }
+
+
+  void LayoutWidget::FitContent()
+  {
+    for (size_t i = 0; i < children_.size(); i++)
+    {
+      children_[i]->GetWidget().FitContent();
+    }
+  }
+  
+
+  void LayoutWidget::NotifyContentChanged(const IWidget& widget)
+  {
+    // One of the children has changed
+    WidgetBase::NotifyContentChanged();
+  }
+
+
+  void LayoutWidget::SetHorizontal()
+  {
+    isHorizontal_ = true;
+    ComputeChildrenExtents();
+  }
+
+
+  void LayoutWidget::SetVertical()
+  {
+    isHorizontal_ = false;
+    ComputeChildrenExtents();
+  }
+
+
+  void LayoutWidget::SetPadding(unsigned int left,
+                                unsigned int top,
+                                unsigned int right,
+                                unsigned int bottom,
+                                unsigned int spacing)
+  {
+    paddingLeft_ = left;
+    paddingTop_ = top;
+    paddingRight_ = right;
+    paddingBottom_ = bottom;
+    paddingInternal_ = spacing;
+  }
+    
+
+  void LayoutWidget::SetPadding(unsigned int padding)
+  {
+    paddingLeft_ = padding;
+    paddingTop_ = padding;
+    paddingRight_ = padding;
+    paddingBottom_ = padding;
+    paddingInternal_ = padding;
+  }
+
+
+  IWidget& LayoutWidget::AddWidget(IWidget* widget)  // Takes ownership
+  {
+    if (widget == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+
+    if (GetStatusBar() != NULL)
+    {
+      widget->SetStatusBar(*GetStatusBar());
+    }
+
+    children_.push_back(new ChildWidget(widget));
+    widget->SetParent(*this);
+
+    ComputeChildrenExtents();
+
+    if (widget->HasAnimation())
+    {
+      hasAnimation_ = true;
+    }
+
+    return *widget;
+  }
+
+
+  void LayoutWidget::SetStatusBar(IStatusBar& statusBar)
+  {
+    WidgetBase::SetStatusBar(statusBar);
+
+    for (size_t i = 0; i < children_.size(); i++)
+    {
+      children_[i]->GetWidget().SetStatusBar(statusBar);
+    }
+  }
+
+
+  void LayoutWidget::SetSize(unsigned int width,
+                             unsigned int height)
+  {
+    width_ = width;
+    height_ = height;
+    ComputeChildrenExtents();
+  }
+
+
+  bool LayoutWidget::Render(Orthanc::ImageAccessor& surface)
+  {
+    if (!WidgetBase::Render(surface))
+    {
+      return false;
+    }
+
+    for (size_t i = 0; i < children_.size(); i++)
+    {
+      if (!children_[i]->Render(surface))
+      {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+    
+  IMouseTracker* LayoutWidget::CreateMouseTracker(OrthancStone::MouseButton button,
+                                                  int x,
+                                                  int y,
+                                                  OrthancStone::KeyboardModifiers modifiers,
+                                                  const std::vector<Touch>& touches)
+  {
+    for (size_t i = 0; i < children_.size(); i++)
+    {
+      IMouseTracker* tracker = children_[i]->CreateMouseTracker(button, x, y, modifiers, touches);
+      if (tracker != NULL)
+      {
+        return tracker;
+      }
+    }
+
+    return NULL;
+  }
+
+
+  void LayoutWidget::RenderMouseOver(Orthanc::ImageAccessor& target,
+                                     int x,
+                                     int y)
+  {
+    for (size_t i = 0; i < children_.size(); i++)
+    {
+      children_[i]->RenderMouseOver(target, x, y);
+    }
+  }
+
+
+  void LayoutWidget::MouseWheel(OrthancStone::MouseWheelDirection direction,
+                                int x,
+                                int y,
+                                OrthancStone::KeyboardModifiers modifiers)
+  {
+    for (size_t i = 0; i < children_.size(); i++)
+    {
+      children_[i]->MouseWheel(direction, x, y, modifiers);
+    }
+  }
+
+
+  void LayoutWidget::KeyPressed(OrthancStone::KeyboardKeys key,
+                                char keyChar,
+                                OrthancStone::KeyboardModifiers modifiers)
+  {
+    for (size_t i = 0; i < children_.size(); i++)
+    {
+      children_[i]->GetWidget().KeyPressed(key, keyChar, modifiers);
+    }
+  }
+
+  
+  void LayoutWidget::DoAnimation()
+  {
+    if (hasAnimation_)
+    {
+      for (size_t i = 0; i < children_.size(); i++)
+      {
+        children_[i]->DoAnimation();
+      }
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+    }
+  }
+
+
+  bool LayoutWidget::HasRenderMouseOver()
+  {
+    for (size_t i = 0; i < children_.size(); i++)
+    {
+      if (children_[i]->HasRenderMouseOver())
+      {
+        return true;
+      }
+    }
+
+    return false;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Widgets/LayoutWidget.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,134 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "WidgetBase.h"
+
+#include <vector>
+#include <memory>
+
+namespace Deprecated
+{
+  class LayoutWidget : public WidgetBase
+  {
+  private:
+    class LayoutMouseTracker;
+    class ChildWidget;
+
+    std::vector<ChildWidget*>     children_;
+    bool                          isHorizontal_;
+    unsigned int                  width_;
+    unsigned int                  height_;
+    std::auto_ptr<IMouseTracker>  mouseTracker_;
+    unsigned int                  paddingLeft_;
+    unsigned int                  paddingTop_;
+    unsigned int                  paddingRight_;
+    unsigned int                  paddingBottom_;
+    unsigned int                  paddingInternal_;
+    bool                          hasAnimation_;
+
+    void ComputeChildrenExtents();
+
+  public:
+    LayoutWidget(const std::string& name);
+
+    virtual ~LayoutWidget();
+
+    virtual void FitContent();
+
+    virtual void NotifyContentChanged(const IWidget& widget);
+
+    void SetHorizontal();
+
+    void SetVertical();
+
+    void SetPadding(unsigned int left,
+                    unsigned int top,
+                    unsigned int right,
+                    unsigned int bottom,
+                    unsigned int spacing);
+    
+    void SetPadding(unsigned int padding);
+
+    unsigned int GetPaddingLeft() const
+    {
+      return paddingLeft_;
+    }
+
+    unsigned int GetPaddingTop() const
+    {
+      return paddingTop_;
+    }
+
+    unsigned int GetPaddingRight() const
+    {
+      return paddingRight_;
+    }
+
+    unsigned int GetPaddingBottom() const
+    {
+      return paddingBottom_;
+    }
+
+    unsigned int GetPaddingInternal() const
+    {
+      return paddingInternal_;
+    }
+
+    IWidget& AddWidget(IWidget* widget);  // Takes ownership
+
+    virtual void SetStatusBar(IStatusBar& statusBar);
+
+    virtual void SetSize(unsigned int width,
+                         unsigned int height);
+
+    virtual bool Render(Orthanc::ImageAccessor& surface);
+    
+    virtual IMouseTracker* CreateMouseTracker(OrthancStone::MouseButton button,
+                                              int x,
+                                              int y,
+                                              OrthancStone::KeyboardModifiers modifiers,
+                                              const std::vector<Touch>& touches);
+
+    virtual void RenderMouseOver(Orthanc::ImageAccessor& target,
+                                 int x,
+                                 int y);
+
+    virtual void MouseWheel(OrthancStone::MouseWheelDirection direction,
+                            int x,
+                            int y,
+                            OrthancStone::KeyboardModifiers modifiers);
+
+    virtual void KeyPressed(OrthancStone::KeyboardKeys key,
+                            char keyChar,
+                            OrthancStone::KeyboardModifiers modifiers);
+
+    virtual bool HasAnimation() const
+    {
+      return hasAnimation_;
+    }
+
+    virtual void DoAnimation();
+
+    virtual bool HasRenderMouseOver();
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Widgets/PanMouseTracker.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,58 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "PanMouseTracker.h"
+
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+
+namespace Deprecated
+{
+  PanMouseTracker::PanMouseTracker(WorldSceneWidget& that,
+                                   int x,
+                                   int y) :
+    that_(that)
+  {
+    that.GetView().GetPan(originalPanX_, originalPanY_);
+    that.GetView().MapPixelCenterToScene(downX_, downY_, x, y);
+  }
+    
+
+  void PanMouseTracker::Render(OrthancStone::CairoContext& context,
+                               double zoom)
+  {
+    throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+  }
+
+
+  void PanMouseTracker::MouseMove(int displayX,
+                                  int displayY,
+                                  double x,
+                                  double y,
+                                  const std::vector<Touch>& displayTouches,
+                                  const std::vector<Touch>& sceneTouches)
+  {
+    ViewportGeometry view = that_.GetView();
+    view.SetPan(originalPanX_ + (x - downX_) * view.GetZoom(),
+                originalPanY_ + (y - downY_) * view.GetZoom());
+    that_.SetView(view);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Widgets/PanMouseTracker.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,61 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "WorldSceneWidget.h"
+
+namespace Deprecated
+{
+  class PanMouseTracker : public IWorldSceneMouseTracker
+  {
+  private:
+    WorldSceneWidget&  that_;
+    double             originalPanX_;
+    double             originalPanY_;
+    double             downX_;
+    double             downY_;
+    
+  public:
+    PanMouseTracker(WorldSceneWidget& that,
+                    int x,
+                    int y);
+    
+    virtual bool HasRender() const
+    {
+      return false;
+    }
+
+    virtual void MouseUp()
+    {
+    }
+
+    virtual void Render(OrthancStone::CairoContext& context,
+                        double zoom);
+
+    virtual void MouseMove(int displayX,
+                           int displayY,
+                           double x,
+                           double y,
+                           const std::vector<Touch>& displayTouches,
+                           const std::vector<Touch>& sceneTouches);
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Widgets/PanZoomMouseTracker.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,137 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "PanZoomMouseTracker.h"
+
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+#include <math.h>
+
+namespace Deprecated
+{
+  Touch GetCenter(const std::vector<Touch>& touches)
+  {
+    return Touch((touches[0].x + touches[1].x) / 2.0f, (touches[0].y + touches[1].y) / 2.0f);
+  }
+
+  double GetDistance(const std::vector<Touch>& touches)
+  {
+    float dx = touches[0].x - touches[1].x;
+    float dy = touches[0].y - touches[1].y;
+    return sqrt((double)(dx * dx) + (double)(dy * dy));
+  }
+
+
+  PanZoomMouseTracker::PanZoomMouseTracker(WorldSceneWidget& that,
+                                           const std::vector<Touch>& startTouches)
+    : that_(that),
+      originalZoom_(that.GetView().GetZoom())
+  {
+    that.GetView().GetPan(originalPanX_, originalPanY_);
+    that.GetView().MapPixelCenterToScene(originalSceneTouches_, startTouches);
+
+    originalDisplayCenter_ = GetCenter(startTouches);
+    originalSceneCenter_ = GetCenter(originalSceneTouches_);
+    originalDisplayDistanceBetweenTouches_ = GetDistance(startTouches);
+
+//    printf("original Pan %f %f\n", originalPanX_, originalPanY_);
+//    printf("original Zoom %f \n", originalZoom_);
+//    printf("original distance %f \n", (float)originalDisplayDistanceBetweenTouches_);
+//    printf("original display touches 0 %f %f\n", startTouches[0].x, startTouches[0].y);
+//    printf("original display touches 1 %f %f\n", startTouches[1].x, startTouches[1].y);
+//    printf("original Scene center %f %f\n", originalSceneCenter_.x, originalSceneCenter_.y);
+
+    unsigned int height = that.GetView().GetDisplayHeight();
+
+    if (height <= 3)
+    {
+      idle_ = true;
+      LOG(WARNING) << "image is too small to zoom (current height = " << height << ")";
+    }
+    else
+    {
+      idle_ = false;
+      normalization_ = 1.0 / static_cast<double>(height - 1);
+    }
+
+  }
+
+
+  void PanZoomMouseTracker::Render(OrthancStone::CairoContext& context,
+                                   double zoom)
+  {
+    throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+  }
+
+
+  void PanZoomMouseTracker::MouseMove(int displayX,
+                                      int displayY,
+                                      double x,
+                                      double y,
+                                      const std::vector<Touch>& displayTouches,
+                                      const std::vector<Touch>& sceneTouches)
+  {
+    ViewportGeometry view = that_.GetView();
+
+//    printf("Display touches 0 %f %f\n", displayTouches[0].x, displayTouches[0].y);
+//    printf("Display touches 1 %f %f\n", displayTouches[1].x, displayTouches[1].y);
+//    printf("Scene touches 0 %f %f\n", sceneTouches[0].x, sceneTouches[0].y);
+//    printf("Scene touches 1 %f %f\n", sceneTouches[1].x, sceneTouches[1].y);
+
+//    printf("zoom = %f\n", view.GetZoom());
+    Touch currentSceneCenter = GetCenter(sceneTouches);
+    double panX = originalPanX_ + (currentSceneCenter.x - originalSceneCenter_.x) * view.GetZoom();
+    double panY = originalPanY_ + (currentSceneCenter.y - originalSceneCenter_.y) * view.GetZoom();
+
+    view.SetPan(panX, panY);
+
+    static const double MIN_ZOOM = -4;
+    static const double MAX_ZOOM = 4;
+
+    if (!idle_)
+    {
+      double currentDistanceBetweenTouches = GetDistance(displayTouches);
+
+      double dy = static_cast<double>(currentDistanceBetweenTouches - originalDisplayDistanceBetweenTouches_) * normalization_;  // In the range [-1,1]
+      double z;
+
+      // Linear interpolation from [-1, 1] to [MIN_ZOOM, MAX_ZOOM]
+      if (dy < -1.0)
+      {
+        z = MIN_ZOOM;
+      }
+      else if (dy > 1.0)
+      {
+        z = MAX_ZOOM;
+      }
+      else
+      {
+        z = MIN_ZOOM + (MAX_ZOOM - MIN_ZOOM) * (dy + 1.0) / 2.0;
+      }
+
+      z = pow(2.0, z);
+
+      view.SetZoom(z * originalZoom_);
+    }
+
+    that_.SetView(view);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Widgets/PanZoomMouseTracker.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,65 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "WorldSceneWidget.h"
+
+namespace Deprecated
+{
+  class PanZoomMouseTracker : public IWorldSceneMouseTracker
+  {
+  private:
+    WorldSceneWidget&  that_;
+    std::vector<Touch> originalSceneTouches_;
+    Touch              originalSceneCenter_;
+    Touch              originalDisplayCenter_;
+    double             originalPanX_;
+    double             originalPanY_;
+    double             originalZoom_;
+    double             originalDisplayDistanceBetweenTouches_;
+    bool               idle_;
+    double             normalization_;
+
+  public:
+    PanZoomMouseTracker(WorldSceneWidget& that,
+                        const std::vector<Touch>& startTouches);
+    
+    virtual bool HasRender() const
+    {
+      return false;
+    }
+
+    virtual void MouseUp()
+    {
+    }
+
+    virtual void Render(OrthancStone::CairoContext& context,
+                        double zoom);
+
+    virtual void MouseMove(int displayX,
+                           int displayY,
+                           double x,
+                           double y,
+                           const std::vector<Touch>& displayTouches,
+                           const std::vector<Touch>& sceneTouches);
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Widgets/SliceViewerWidget.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,654 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "SliceViewerWidget.h"
+
+#include "../Layers/SliceOutlineRenderer.h"
+#include "../../Toolbox/GeometryToolbox.h"
+#include "../Layers/FrameRenderer.h"
+
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+
+#include <boost/math/constants/constants.hpp>
+
+
+static const double THIN_SLICE_THICKNESS = 100.0 * std::numeric_limits<double>::epsilon();
+
+namespace Deprecated
+{
+  class SliceViewerWidget::Scene : public boost::noncopyable
+  {
+  private:
+    OrthancStone::CoordinateSystem3D            plane_;
+    double                        thickness_;
+    size_t                        countMissing_;
+    std::vector<ILayerRenderer*>  renderers_;
+
+  public:
+    void DeleteLayer(size_t index)
+    {
+      if (index >= renderers_.size())
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+      }
+
+      assert(countMissing_ <= renderers_.size());
+
+      if (renderers_[index] != NULL)
+      {
+        assert(countMissing_ < renderers_.size());
+        delete renderers_[index];
+        renderers_[index] = NULL;
+        countMissing_++;
+      }
+    }
+
+    Scene(const OrthancStone::CoordinateSystem3D& plane,
+          double thickness,
+          size_t countLayers) :
+      plane_(plane),
+      thickness_(thickness),
+      countMissing_(countLayers),
+      renderers_(countLayers, NULL)
+    {
+      if (thickness <= 0)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+      }
+    }
+
+    ~Scene()
+    {
+      for (size_t i = 0; i < renderers_.size(); i++)
+      {
+        DeleteLayer(i);
+      }
+    }
+
+    void SetLayer(size_t index,
+                  ILayerRenderer* renderer)  // Takes ownership
+    {
+      if (renderer == NULL)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+      }
+
+      DeleteLayer(index);
+
+      renderers_[index] = renderer;
+      countMissing_--;
+    }
+
+    const OrthancStone::CoordinateSystem3D& GetPlane() const
+    {
+      return plane_;
+    }
+
+    bool HasRenderer(size_t index)
+    {
+      return renderers_[index] != NULL;
+    }
+
+    bool IsComplete() const
+    {
+      return countMissing_ == 0;
+    }
+
+    unsigned int GetCountMissing() const
+    {
+      return static_cast<unsigned int>(countMissing_);
+    }
+
+    bool RenderScene(OrthancStone::CairoContext& context,
+                     const ViewportGeometry& view,
+                     const OrthancStone::CoordinateSystem3D& viewportPlane)
+    {
+      bool fullQuality = true;
+      cairo_t *cr = context.GetObject();
+
+      for (size_t i = 0; i < renderers_.size(); i++)
+      {
+        if (renderers_[i] != NULL)
+        {
+          const OrthancStone::CoordinateSystem3D& framePlane = renderers_[i]->GetLayerPlane();
+          
+          double x0, y0, x1, y1, x2, y2;
+          viewportPlane.ProjectPoint(x0, y0, framePlane.GetOrigin());
+          viewportPlane.ProjectPoint(x1, y1, framePlane.GetOrigin() + framePlane.GetAxisX());
+          viewportPlane.ProjectPoint(x2, y2, framePlane.GetOrigin() + framePlane.GetAxisY());
+
+          /**
+           * Now we solve the system of linear equations Ax + b = x', given:
+           *   A [0 ; 0] + b = [x0 ; y0]
+           *   A [1 ; 0] + b = [x1 ; y1]
+           *   A [0 ; 1] + b = [x2 ; y2]
+           * <=>
+           *   b = [x0 ; y0]
+           *   A [1 ; 0] = [x1 ; y1] - b = [x1 - x0 ; y1 - y0]
+           *   A [0 ; 1] = [x2 ; y2] - b = [x2 - x0 ; y2 - y0]
+           * <=>
+           *   b = [x0 ; y0]
+           *   [a11 ; a21] = [x1 - x0 ; y1 - y0]
+           *   [a12 ; a22] = [x2 - x0 ; y2 - y0]
+           **/
+
+          cairo_matrix_t transform;
+          cairo_matrix_init(&transform, x1 - x0, y1 - y0, x2 - x0, y2 - y0, x0, y0);
+
+          cairo_save(cr);
+          cairo_transform(cr, &transform);
+          
+          if (!renderers_[i]->RenderLayer(context, view))
+          {
+            cairo_restore(cr);
+            return false;
+          }
+
+          cairo_restore(cr);
+        }
+
+        if (renderers_[i] != NULL &&
+            !renderers_[i]->IsFullQuality())
+        {
+          fullQuality = false;
+        }
+      }
+
+      if (!fullQuality)
+      {
+        double x, y;
+        view.MapDisplayToScene(x, y, static_cast<double>(view.GetDisplayWidth()) / 2.0, 10);
+
+        cairo_translate(cr, x, y);
+
+#if 1
+        double s = 5.0 / view.GetZoom();
+        cairo_rectangle(cr, -s, -s, 2.0 * s, 2.0 * s);
+#else
+        // TODO Drawing filled circles makes WebAssembly crash!
+        cairo_arc(cr, 0, 0, 5.0 / view.GetZoom(), 0, 2.0 * boost::math::constants::pi<double>());
+#endif
+        
+        cairo_set_line_width(cr, 2.0 / view.GetZoom());
+        cairo_set_source_rgb(cr, 1, 1, 1);
+        cairo_stroke_preserve(cr);
+        cairo_set_source_rgb(cr, 1, 0, 0);
+        cairo_fill(cr);
+      }
+
+      return true;
+    }
+
+    void SetLayerStyle(size_t index,
+                       const RenderStyle& style)
+    {
+      if (renderers_[index] != NULL)
+      {
+        renderers_[index]->SetLayerStyle(style);
+      }
+    }
+
+    bool ContainsPlane(const OrthancStone::CoordinateSystem3D& plane) const
+    {
+      bool isOpposite;
+      if (!OrthancStone::GeometryToolbox::IsParallelOrOpposite(isOpposite,
+                                                               plane.GetNormal(),
+                                                               plane_.GetNormal()))
+      {
+        return false;
+      }
+      else
+      {
+        double z = (plane_.ProjectAlongNormal(plane.GetOrigin()) -
+                    plane_.ProjectAlongNormal(plane_.GetOrigin()));
+
+        if (z < 0)
+        {
+          z = -z;
+        }
+
+        return z <= thickness_;
+      }
+    }
+
+    double GetThickness() const
+    {
+      return thickness_;
+    }
+  };
+
+  
+  bool SliceViewerWidget::LookupLayer(size_t& index /* out */,
+                                      const IVolumeSlicer& layer) const
+  {
+    LayersIndex::const_iterator found = layersIndex_.find(&layer);
+
+    if (found == layersIndex_.end())
+    {
+      return false;
+    }
+    else
+    {
+      index = found->second;
+      assert(index < layers_.size() &&
+             layers_[index] == &layer);
+      return true;
+    }
+  }
+
+
+  void SliceViewerWidget::GetLayerExtent(OrthancStone::Extent2D& extent,
+                                         IVolumeSlicer& source) const
+  {
+    extent.Reset();
+
+    std::vector<OrthancStone::Vector> points;
+    if (source.GetExtent(points, plane_))
+    {
+      for (size_t i = 0; i < points.size(); i++)
+      {
+        double x, y;
+        plane_.ProjectPoint(x, y, points[i]);
+        extent.AddPoint(x, y);
+      }
+    }
+  }
+
+
+  OrthancStone::Extent2D SliceViewerWidget::GetSceneExtent()
+  {
+    OrthancStone::Extent2D sceneExtent;
+
+    for (size_t i = 0; i < layers_.size(); i++)
+    {
+      assert(layers_[i] != NULL);
+      OrthancStone::Extent2D layerExtent;
+      GetLayerExtent(layerExtent, *layers_[i]);
+
+      sceneExtent.Union(layerExtent);
+    }
+
+    return sceneExtent;
+  }
+
+  
+  bool SliceViewerWidget::RenderScene(OrthancStone::CairoContext& context,
+                                      const ViewportGeometry& view)
+  {
+    if (currentScene_.get() != NULL)
+    {
+      return currentScene_->RenderScene(context, view, plane_);
+    }
+    else
+    {
+      return true;
+    }
+  }
+
+  
+  void SliceViewerWidget::ResetPendingScene()
+  {
+    double thickness;
+    if (pendingScene_.get() == NULL)
+    {
+      thickness = 1.0;
+    }
+    else
+    {
+      thickness = pendingScene_->GetThickness();
+    }
+    
+    pendingScene_.reset(new Scene(plane_, thickness, layers_.size()));
+  }
+  
+
+  void SliceViewerWidget::UpdateLayer(size_t index,
+                                      ILayerRenderer* renderer,
+                                      const OrthancStone::CoordinateSystem3D& plane)
+  {
+    LOG(INFO) << "Updating layer " << index;
+    
+    std::auto_ptr<ILayerRenderer> tmp(renderer);
+
+    if (renderer == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+    }
+
+    if (index >= layers_.size())
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+
+    assert(layers_.size() == styles_.size());
+    renderer->SetLayerStyle(styles_[index]);
+
+    if (currentScene_.get() != NULL &&
+        currentScene_->ContainsPlane(plane))
+    {
+      currentScene_->SetLayer(index, tmp.release());
+      NotifyContentChanged();
+    }
+    else if (pendingScene_.get() != NULL &&
+             pendingScene_->ContainsPlane(plane))
+    {
+      pendingScene_->SetLayer(index, tmp.release());
+
+      if (currentScene_.get() == NULL ||
+          !currentScene_->IsComplete() ||
+          pendingScene_->IsComplete())
+      {
+        currentScene_ = pendingScene_;
+        NotifyContentChanged();
+      }
+    }
+  }
+
+  
+  SliceViewerWidget::SliceViewerWidget(OrthancStone::MessageBroker& broker, 
+                                       const std::string& name) :
+    WorldSceneWidget(name),
+    IObserver(broker),
+    IObservable(broker),
+    started_(false)
+  {
+    SetBackgroundCleared(true);
+  }
+  
+  
+  SliceViewerWidget::~SliceViewerWidget()
+  {
+    for (size_t i = 0; i < layers_.size(); i++)
+    {
+      delete layers_[i];
+    }
+  }
+  
+  void SliceViewerWidget::ObserveLayer(IVolumeSlicer& layer)
+  {
+    layer.RegisterObserverCallback(new OrthancStone::Callable<SliceViewerWidget, IVolumeSlicer::GeometryReadyMessage>
+                                   (*this, &SliceViewerWidget::OnGeometryReady));
+    // currently ignore errors layer->RegisterObserverCallback(new Callable<SliceViewerWidget, IVolumeSlicer::GeometryErrorMessage>(*this, &SliceViewerWidget::...));
+    layer.RegisterObserverCallback(new OrthancStone::Callable<SliceViewerWidget, IVolumeSlicer::SliceContentChangedMessage>
+                                   (*this, &SliceViewerWidget::OnSliceChanged));
+    layer.RegisterObserverCallback(new OrthancStone::Callable<SliceViewerWidget, IVolumeSlicer::ContentChangedMessage>
+                                   (*this, &SliceViewerWidget::OnContentChanged));
+    layer.RegisterObserverCallback(new OrthancStone::Callable<SliceViewerWidget, IVolumeSlicer::LayerReadyMessage>
+                                   (*this, &SliceViewerWidget::OnLayerReady));
+    layer.RegisterObserverCallback(new OrthancStone::Callable<SliceViewerWidget, IVolumeSlicer::LayerErrorMessage>
+                                   (*this, &SliceViewerWidget::OnLayerError));
+  }
+
+
+  size_t SliceViewerWidget::AddLayer(IVolumeSlicer* layer)  // Takes ownership
+  {
+    if (layer == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+    }
+
+    size_t index = layers_.size();
+    layers_.push_back(layer);
+    styles_.push_back(RenderStyle());
+    layersIndex_[layer] = index;
+
+    ResetPendingScene();
+
+    ObserveLayer(*layer);
+
+    ResetChangedLayers();
+
+    return index;
+  }
+
+
+  void SliceViewerWidget::ReplaceLayer(size_t index, IVolumeSlicer* layer)  // Takes ownership
+  {
+    if (layer == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+    }
+
+    if (index >= layers_.size())
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+
+    delete layers_[index];
+    layers_[index] = layer;
+    layersIndex_[layer] = index;
+
+    ResetPendingScene();
+
+    ObserveLayer(*layer);
+
+    InvalidateLayer(index);
+  }
+
+
+  void SliceViewerWidget::RemoveLayer(size_t index)
+  {
+    if (index >= layers_.size())
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+
+    IVolumeSlicer* previousLayer = layers_[index];
+    layersIndex_.erase(layersIndex_.find(previousLayer));
+    layers_.erase(layers_.begin() + index);
+    changedLayers_.erase(changedLayers_.begin() + index);
+    styles_.erase(styles_.begin() + index);
+
+    delete layers_[index];
+
+    currentScene_->DeleteLayer(index);
+    ResetPendingScene();
+
+    NotifyContentChanged();
+  }
+
+
+  const RenderStyle& SliceViewerWidget::GetLayerStyle(size_t layer) const
+  {
+    if (layer >= layers_.size())
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+
+    assert(layers_.size() == styles_.size());
+    return styles_[layer];
+  }
+  
+
+  void SliceViewerWidget::SetLayerStyle(size_t layer,
+                                        const RenderStyle& style)
+  {
+    if (layer >= layers_.size())
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+
+    assert(layers_.size() == styles_.size());
+    styles_[layer] = style;
+
+    if (currentScene_.get() != NULL)
+    {
+      currentScene_->SetLayerStyle(layer, style);
+    }
+
+    if (pendingScene_.get() != NULL)
+    {
+      pendingScene_->SetLayerStyle(layer, style);
+    }
+
+    NotifyContentChanged();
+  }
+  
+
+  void SliceViewerWidget::SetSlice(const OrthancStone::CoordinateSystem3D& plane)
+  {
+    LOG(INFO) << "Setting slice origin: (" << plane.GetOrigin()[0]
+              << "," << plane.GetOrigin()[1]
+              << "," << plane.GetOrigin()[2] << ")";
+    
+    Deprecated::Slice displayedSlice(plane_, THIN_SLICE_THICKNESS);
+
+    //if (!displayedSlice.ContainsPlane(slice))
+    {
+      if (currentScene_.get() == NULL ||
+          (pendingScene_.get() != NULL &&
+           pendingScene_->IsComplete()))
+      {
+        currentScene_ = pendingScene_;
+      }
+
+      plane_ = plane;
+      ResetPendingScene();
+
+      InvalidateAllLayers();   // TODO Removing this line avoid loading twice the image in WASM
+    }
+
+    BroadcastMessage(DisplayedSliceMessage(*this, displayedSlice));
+  }
+
+
+  void SliceViewerWidget::OnGeometryReady(const IVolumeSlicer::GeometryReadyMessage& message)
+  {
+    size_t i;
+    if (LookupLayer(i, message.GetOrigin()))
+    {
+      LOG(INFO) << ": Geometry ready for layer " << i << " in " << GetName();
+
+      changedLayers_[i] = true;
+      //layers_[i]->ScheduleLayerCreation(plane_);
+    }
+    BroadcastMessage(GeometryChangedMessage(*this));
+  }
+  
+
+  void SliceViewerWidget::InvalidateAllLayers()
+  {
+    for (size_t i = 0; i < layers_.size(); i++)
+    {
+      assert(layers_[i] != NULL);
+      changedLayers_[i] = true;
+      
+      //layers_[i]->ScheduleLayerCreation(plane_);
+    }
+  }
+
+
+  void SliceViewerWidget::InvalidateLayer(size_t layer)
+  {
+    if (layer >= layers_.size())
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+
+    assert(layers_[layer] != NULL);
+    changedLayers_[layer] = true;
+
+    //layers_[layer]->ScheduleLayerCreation(plane_);
+  }
+
+
+  void SliceViewerWidget::OnContentChanged(const IVolumeSlicer::ContentChangedMessage& message)
+  {
+    size_t index;
+    if (LookupLayer(index, message.GetOrigin()))
+    {
+      InvalidateLayer(index);
+    }
+    
+    BroadcastMessage(SliceViewerWidget::ContentChangedMessage(*this));
+  }
+  
+
+  void SliceViewerWidget::OnSliceChanged(const IVolumeSlicer::SliceContentChangedMessage& message)
+  {
+    if (message.GetSlice().ContainsPlane(plane_))
+    {
+      size_t index;
+      if (LookupLayer(index, message.GetOrigin()))
+      {
+        InvalidateLayer(index);
+      }
+    }
+    
+    BroadcastMessage(SliceViewerWidget::ContentChangedMessage(*this));
+  }
+  
+  
+  void SliceViewerWidget::OnLayerReady(const IVolumeSlicer::LayerReadyMessage& message)
+  {
+    size_t index;
+    if (LookupLayer(index, message.GetOrigin()))
+    {
+      LOG(INFO) << "Renderer ready for layer " << index;
+      UpdateLayer(index, message.CreateRenderer(), message.GetSlice());
+    }
+    
+    BroadcastMessage(SliceViewerWidget::ContentChangedMessage(*this));
+  }
+
+
+  void SliceViewerWidget::OnLayerError(const IVolumeSlicer::LayerErrorMessage& message)
+  {
+    size_t index;
+    if (LookupLayer(index, message.GetOrigin()))
+    {
+      LOG(ERROR) << "Using error renderer on layer " << index;
+
+      // TODO
+      //UpdateLayer(index, new SliceOutlineRenderer(slice), slice);
+
+      BroadcastMessage(SliceViewerWidget::ContentChangedMessage(*this));
+    }
+  }
+
+
+  void SliceViewerWidget::ResetChangedLayers()
+  {
+    changedLayers_.resize(layers_.size());
+
+    for (size_t i = 0; i < changedLayers_.size(); i++)
+    {
+      changedLayers_[i] = false;
+    }
+  }
+
+
+  void SliceViewerWidget::DoAnimation()
+  {
+    assert(changedLayers_.size() <= layers_.size());
+    
+    for (size_t i = 0; i < changedLayers_.size(); i++)
+    {
+      if (changedLayers_[i])
+      {
+        layers_[i]->ScheduleLayerCreation(plane_);
+      }
+    }
+    
+    ResetChangedLayers();
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Widgets/SliceViewerWidget.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,155 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "WorldSceneWidget.h"
+#include "../Layers/IVolumeSlicer.h"
+#include "../../Toolbox/Extent2D.h"
+#include "../../Messages/IObserver.h"
+
+#include <map>
+
+namespace Deprecated
+{
+  class SliceViewerWidget :
+    public WorldSceneWidget,
+    public OrthancStone::IObserver,
+    public OrthancStone::IObservable
+  {
+  public:
+    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryChangedMessage, SliceViewerWidget);
+    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, ContentChangedMessage, SliceViewerWidget);
+
+
+    // TODO - Use this message in ReferenceLineSource
+    class DisplayedSliceMessage : public OrthancStone::OriginMessage<SliceViewerWidget>
+    {
+      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
+      
+    private:
+      const Deprecated::Slice& slice_;
+
+    public:
+      DisplayedSliceMessage(SliceViewerWidget& origin,
+                            const Deprecated::Slice& slice) :
+        OriginMessage(origin),
+        slice_(slice)
+      {
+      }
+
+      const Deprecated::Slice& GetSlice() const
+      {
+        return slice_;
+      }
+    };
+
+  private:
+    SliceViewerWidget(const SliceViewerWidget&);
+    SliceViewerWidget& operator=(const SliceViewerWidget&);
+
+    class Scene;
+    
+    typedef std::map<const IVolumeSlicer*, size_t>  LayersIndex;
+
+    bool                         started_;
+    LayersIndex                  layersIndex_;
+    std::vector<IVolumeSlicer*>  layers_;
+    std::vector<RenderStyle>     styles_;
+    OrthancStone::CoordinateSystem3D           plane_;
+    std::auto_ptr<Scene>         currentScene_;
+    std::auto_ptr<Scene>         pendingScene_;
+    std::vector<bool>            changedLayers_;
+
+    bool LookupLayer(size_t& index /* out */,
+                     const IVolumeSlicer& layer) const;
+
+    void GetLayerExtent(OrthancStone::Extent2D& extent,
+                        IVolumeSlicer& source) const;
+
+    void OnGeometryReady(const IVolumeSlicer::GeometryReadyMessage& message);
+
+    virtual void OnContentChanged(const IVolumeSlicer::ContentChangedMessage& message);
+
+    virtual void OnSliceChanged(const IVolumeSlicer::SliceContentChangedMessage& message);
+
+    virtual void OnLayerReady(const IVolumeSlicer::LayerReadyMessage& message);
+
+    virtual void OnLayerError(const IVolumeSlicer::LayerErrorMessage& message);
+
+    void ObserveLayer(IVolumeSlicer& source);
+
+    void ResetChangedLayers();
+
+  public:
+    SliceViewerWidget(OrthancStone::MessageBroker& broker, 
+                      const std::string& name);
+
+    virtual OrthancStone::Extent2D GetSceneExtent();
+
+  protected:
+    virtual bool RenderScene(OrthancStone::CairoContext& context,
+                             const ViewportGeometry& view);
+
+    void ResetPendingScene();
+
+    void UpdateLayer(size_t index,
+                     ILayerRenderer* renderer,
+                     const OrthancStone::CoordinateSystem3D& plane);
+
+    void InvalidateAllLayers();
+
+    void InvalidateLayer(size_t layer);
+    
+  public:
+    virtual ~SliceViewerWidget();
+
+    size_t AddLayer(IVolumeSlicer* layer);  // Takes ownership
+
+    void ReplaceLayer(size_t layerIndex, IVolumeSlicer* layer); // Takes ownership
+
+    void RemoveLayer(size_t layerIndex);
+
+    size_t GetLayerCount() const
+    {
+      return layers_.size();
+    }
+
+    const RenderStyle& GetLayerStyle(size_t layer) const;
+
+    void SetLayerStyle(size_t layer,
+                       const RenderStyle& style);
+
+    void SetSlice(const OrthancStone::CoordinateSystem3D& plane);
+
+    const OrthancStone::CoordinateSystem3D& GetSlice() const
+    {
+      return plane_;
+    }
+
+    virtual bool HasAnimation() const
+    {
+      return true;
+    }
+
+    virtual void DoAnimation();
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Widgets/TestCairoWidget.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,126 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "TestCairoWidget.h"
+
+#include <stdio.h>
+
+
+namespace Deprecated
+{
+  namespace Samples
+  {
+    void TestCairoWidget::DoAnimation() 
+    {
+      value_ -= 0.01f;
+      if (value_ < 0)
+      {
+        value_ = 1;
+      }
+
+      NotifyContentChanged();
+    }
+
+
+    bool TestCairoWidget::RenderCairo(OrthancStone::CairoContext& context)
+    {
+      cairo_t* cr = context.GetObject();
+
+      cairo_set_source_rgb (cr, .3, 0, 0);
+      cairo_paint(cr);
+
+      cairo_set_source_rgb(cr, 0, 1, 0);
+      cairo_rectangle(cr, width_ / 4, height_ / 4, width_ / 2, height_ / 2);
+      cairo_set_line_width(cr, 1.0);
+      cairo_fill(cr);
+
+      cairo_set_source_rgb(cr, 0, 1, value_);
+      cairo_rectangle(cr, width_ / 2 - 50, height_ / 2 - 50, 100, 100);
+      cairo_fill(cr);
+
+      return true;
+    }
+
+
+    void TestCairoWidget::RenderMouseOverCairo(OrthancStone::CairoContext& context,
+                                               int x,
+                                               int y)
+    {
+      cairo_t* cr = context.GetObject();
+
+      cairo_set_source_rgb (cr, 1, 0, 0);
+      cairo_rectangle(cr, x - 5, y - 5, 10, 10);
+      cairo_set_line_width(cr, 1.0);
+      cairo_stroke(cr);
+
+      char buf[64];
+      sprintf(buf, "(%d,%d)", x, y);
+      UpdateStatusBar(buf);
+    }
+
+
+    TestCairoWidget::TestCairoWidget(const std::string& name, bool animate) :
+      CairoWidget(name),
+      width_(0),
+      height_(0),
+      value_(1),
+      animate_(animate)
+    {
+    }
+
+
+    void TestCairoWidget::SetSize(unsigned int width, 
+                                  unsigned int height)
+    {
+      CairoWidget::SetSize(width, height);
+      width_ = width;
+      height_ = height;
+    }
+ 
+
+    IMouseTracker* TestCairoWidget::CreateMouseTracker(OrthancStone::MouseButton button,
+                                                       int x,
+                                                       int y,
+                                                       OrthancStone::KeyboardModifiers modifiers,
+                                                       const std::vector<Touch>& touches)
+    {
+      UpdateStatusBar("Click");
+      return NULL;
+    }
+
+
+    void TestCairoWidget::MouseWheel(OrthancStone::MouseWheelDirection direction,
+                                     int x,
+                                     int y,
+                                     OrthancStone::KeyboardModifiers modifiers) 
+    {
+      UpdateStatusBar(direction == OrthancStone::MouseWheelDirection_Down ? "Wheel down" : "Wheel up");
+    }
+
+    
+    void TestCairoWidget::KeyPressed(OrthancStone::KeyboardKeys key,
+                                     char keyChar,
+                                     OrthancStone::KeyboardModifiers modifiers)
+    {
+      UpdateStatusBar("Key pressed: \"" + std::string(1, keyChar) + "\"");
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Widgets/TestCairoWidget.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,79 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "CairoWidget.h"
+
+namespace Deprecated
+{
+  namespace Samples
+  {
+    class TestCairoWidget : public CairoWidget
+    {
+    private:
+      unsigned int  width_;
+      unsigned int  height_;
+      float         value_;
+      bool          animate_;
+
+    protected:
+      virtual bool RenderCairo(OrthancStone::CairoContext& context);
+
+      virtual void RenderMouseOverCairo(OrthancStone::CairoContext& context,
+                                        int x,
+                                        int y);
+
+    public:
+      TestCairoWidget(const std::string& name, bool animate);
+
+      virtual void SetSize(unsigned int width, 
+                           unsigned int height);
+ 
+      virtual IMouseTracker* CreateMouseTracker(OrthancStone::MouseButton button,
+                                                int x,
+                                                int y,
+                                                OrthancStone::KeyboardModifiers modifiers,
+                                                const std::vector<Touch>& touches);
+
+      virtual void MouseWheel(OrthancStone::MouseWheelDirection direction,
+                              int x,
+                              int y,
+                              OrthancStone::KeyboardModifiers modifiers);
+    
+      virtual void KeyPressed(OrthancStone::KeyboardKeys key,
+                              char keyChar,
+                              OrthancStone::KeyboardModifiers modifiers);
+
+      virtual bool HasAnimation() const
+      {
+        return animate_;
+      }
+      
+      virtual void DoAnimation();
+
+      virtual bool HasRenderMouseOver()
+      {
+        return true;
+      }
+    };
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Widgets/TestWorldSceneWidget.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,149 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "TestWorldSceneWidget.h"
+
+#include <Core/OrthancException.h>
+
+#include <math.h>
+#include <stdio.h>
+
+namespace Deprecated
+{
+  namespace Samples
+  {
+    class TestWorldSceneWidget::Interactor : public IWorldSceneInteractor
+    {
+    public:
+      virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget,
+                                                          const ViewportGeometry& view,
+                                                          OrthancStone::MouseButton button,
+                                                          OrthancStone::KeyboardModifiers modifiers,
+                                                          int viewportX,
+                                                          int viewportY,
+                                                          double x,
+                                                          double y,
+                                                          IStatusBar* statusBar,
+                                                          const std::vector<Touch>& touches)
+      {
+        if (statusBar)
+        {
+          char buf[64];
+          sprintf(buf, "X = %0.2f, Y = %0.2f", x, y);
+          statusBar->SetMessage(buf);
+        }
+
+        return NULL;
+      }
+
+      virtual void MouseOver(OrthancStone::CairoContext& context,
+                             WorldSceneWidget& widget,
+                             const ViewportGeometry& view,
+                             double x,
+                             double y,
+                             IStatusBar* statusBar)
+      {
+        double S = 0.5;
+
+        if (fabs(x) <= S &&
+            fabs(y) <= S)
+        {
+          cairo_t* cr = context.GetObject();
+          cairo_set_source_rgb(cr, 1, 0, 0);
+          cairo_rectangle(cr, -S, -S , 2.0 * S, 2.0 * S);
+          cairo_set_line_width(cr, 1.0 / view.GetZoom());
+          cairo_stroke(cr);
+        }
+      }
+
+      virtual void MouseWheel(WorldSceneWidget& widget,
+                              OrthancStone::MouseWheelDirection direction,
+                              OrthancStone::KeyboardModifiers modifiers,
+                              IStatusBar* statusBar)
+      {
+        if (statusBar)
+        {
+          statusBar->SetMessage(direction == OrthancStone::MouseWheelDirection_Down ? "Wheel down" : "Wheel up");
+        }
+      }
+
+      virtual void KeyPressed(WorldSceneWidget& widget,
+                              OrthancStone::KeyboardKeys key,
+                              char keyChar,
+                              OrthancStone::KeyboardModifiers modifiers,
+                              IStatusBar* statusBar)
+      {
+        if (statusBar)
+        {
+          statusBar->SetMessage("Key pressed: \"" + std::string(1, keyChar) + "\"");
+        }
+      }
+    };
+
+
+    bool TestWorldSceneWidget::RenderScene(OrthancStone::CairoContext& context,
+                                           const ViewportGeometry& view)
+    {
+      cairo_t* cr = context.GetObject();
+
+      // Clear background
+      cairo_set_source_rgb(cr, 0, 0, 0);
+      cairo_paint(cr);
+
+      float color = static_cast<float>(count_ % 16) / 15.0f;
+      cairo_set_source_rgb(cr, 0, 1.0f - color, color);
+      cairo_rectangle(cr, -10, -.5, 20, 1);
+      cairo_fill(cr);
+
+      return true;
+    }
+
+
+    TestWorldSceneWidget::TestWorldSceneWidget(const std::string& name, bool animate) :
+      WorldSceneWidget(name),
+      interactor_(new Interactor),
+      animate_(animate),
+      count_(0)
+    {
+      SetInteractor(*interactor_);
+    }
+
+
+    OrthancStone::Extent2D TestWorldSceneWidget::GetSceneExtent()
+    {
+      return OrthancStone::Extent2D(-10, -.5, 10, .5);
+    }
+
+
+    void TestWorldSceneWidget::DoAnimation()
+    {
+      if (animate_)
+      {
+        count_++;
+        NotifyContentChanged();
+      }
+      else
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+      }
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Widgets/TestWorldSceneWidget.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,63 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "WorldSceneWidget.h"
+
+#include <memory>
+
+namespace Deprecated
+{
+  namespace Samples
+  {
+    class TestWorldSceneWidget : public WorldSceneWidget
+    {
+    private:
+      class Interactor;
+
+      std::auto_ptr<Interactor>   interactor_;
+      bool                        animate_;
+      unsigned int                count_;
+
+    protected:
+      virtual bool RenderScene(OrthancStone::CairoContext& context,
+                               const ViewportGeometry& view);
+
+    public:
+      TestWorldSceneWidget(const std::string& name, bool animate);
+
+      virtual OrthancStone::Extent2D GetSceneExtent();
+
+      virtual bool HasAnimation() const
+      {
+        return animate_;
+      }
+
+      virtual void DoAnimation();
+
+      virtual bool HasRenderMouseOver()
+      {
+        return true;
+      }
+    };
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Widgets/WidgetBase.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,166 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "WidgetBase.h"
+
+#include <Core/OrthancException.h>
+#include <Core/Images/ImageProcessing.h>
+#include <Core/Logging.h>
+
+namespace Deprecated
+{
+  void WidgetBase::NotifyContentChanged()
+  {
+    if (parent_ != NULL)
+    {
+      parent_->NotifyContentChanged();
+    }
+
+    if (viewport_ != NULL)
+    {
+      viewport_->NotifyBackgroundChanged();
+    }
+  }
+
+
+  void WidgetBase::SetParent(IWidget& parent)
+  {
+    if (parent_ != NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+    else
+    {
+      parent_ = &parent;
+    }
+  }    
+
+  
+  void WidgetBase::ClearBackgroundOrthanc(Orthanc::ImageAccessor& target) const 
+  {
+    // Clear the background using Orthanc
+
+    if (backgroundCleared_)
+    {
+      Orthanc::ImageProcessing::Set(target, 
+                                    backgroundColor_[0],
+                                    backgroundColor_[1],
+                                    backgroundColor_[2],
+                                    255 /* alpha */);
+    }
+  }
+
+
+  void WidgetBase::ClearBackgroundCairo(OrthancStone::CairoContext& context) const
+  {
+    // Clear the background using Cairo
+
+    if (IsBackgroundCleared())
+    {
+      uint8_t red, green, blue;
+      GetBackgroundColor(red, green, blue);
+
+      context.SetSourceColor(red, green, blue);
+      cairo_paint(context.GetObject());
+    }
+  }
+
+
+  void WidgetBase::ClearBackgroundCairo(Orthanc::ImageAccessor& target) const
+  {
+    OrthancStone::CairoSurface surface(target, false /* no alpha */);
+    OrthancStone::CairoContext context(surface);
+    ClearBackgroundCairo(context);
+  }
+
+
+  void WidgetBase::UpdateStatusBar(const std::string& message)
+  {
+    if (statusBar_ != NULL)
+    {
+      statusBar_->SetMessage(message);
+    }
+  }
+
+
+  WidgetBase::WidgetBase(const std::string& name) :
+    parent_(NULL),
+    viewport_(NULL),
+    statusBar_(NULL),
+    backgroundCleared_(false),
+    transmitMouseOver_(false),
+    name_(name)
+  {
+    backgroundColor_[0] = 0;
+    backgroundColor_[1] = 0;
+    backgroundColor_[2] = 0;
+  }
+
+
+  void WidgetBase::SetViewport(WidgetViewport& viewport)
+  {
+    if (viewport_ != NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+    else
+    {
+      viewport_ = &viewport;
+    }
+  }
+
+  
+  void WidgetBase::SetBackgroundColor(uint8_t red,
+                                      uint8_t green,
+                                      uint8_t blue)
+  {
+    backgroundColor_[0] = red;
+    backgroundColor_[1] = green;
+    backgroundColor_[2] = blue;
+  }
+
+  void WidgetBase::GetBackgroundColor(uint8_t& red,
+                                      uint8_t& green,
+                                      uint8_t& blue) const
+  {
+    red = backgroundColor_[0];
+    green = backgroundColor_[1];
+    blue = backgroundColor_[2];
+  }
+
+
+  bool WidgetBase::Render(Orthanc::ImageAccessor& surface)
+  {
+#if 0
+    ClearBackgroundOrthanc(surface);
+#else
+    ClearBackgroundCairo(surface);  // Faster than Orthanc
+#endif
+
+    return true;
+  }
+
+  
+  void WidgetBase::DoAnimation()
+  {
+    throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Widgets/WidgetBase.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,117 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "IWidget.h"
+
+#include "../../Viewport/CairoContext.h"
+#include "../Viewport/WidgetViewport.h"
+
+namespace Deprecated
+{
+  class WidgetBase : public IWidget
+  {
+  private:
+    IWidget*         parent_;
+    WidgetViewport*  viewport_;
+    IStatusBar*      statusBar_;
+    bool             backgroundCleared_;
+    uint8_t          backgroundColor_[3];
+    bool             transmitMouseOver_;
+    std::string      name_;
+
+  protected:
+    void ClearBackgroundOrthanc(Orthanc::ImageAccessor& target) const;
+
+    void ClearBackgroundCairo(OrthancStone::CairoContext& context) const;
+
+    void ClearBackgroundCairo(Orthanc::ImageAccessor& target) const;
+
+    void UpdateStatusBar(const std::string& message);
+
+    IStatusBar* GetStatusBar() const
+    {
+      return statusBar_;
+    }
+
+  public:
+    WidgetBase(const std::string& name);
+
+    virtual void FitContent()
+    {
+    }
+  
+    virtual void SetParent(IWidget& parent);
+    
+    virtual void SetViewport(WidgetViewport& viewport);
+
+    void SetBackgroundCleared(bool clear)
+    {
+      backgroundCleared_ = clear;
+    }
+
+    bool IsBackgroundCleared() const
+    {
+      return backgroundCleared_;
+    }
+
+    void SetTransmitMouseOver(bool transmit)
+    {
+      transmitMouseOver_ = transmit;
+    }
+
+    void SetBackgroundColor(uint8_t red,
+                            uint8_t green,
+                            uint8_t blue);
+
+    void GetBackgroundColor(uint8_t& red,
+                            uint8_t& green,
+                            uint8_t& blue) const;
+
+    virtual void SetStatusBar(IStatusBar& statusBar)
+    {
+      statusBar_ = &statusBar;
+    }
+
+    virtual bool Render(Orthanc::ImageAccessor& surface);
+
+    virtual bool HasAnimation() const
+    {
+      return false;
+    }
+
+    virtual void DoAnimation();
+
+    virtual bool HasRenderMouseOver()
+    {
+      return transmitMouseOver_;
+    }
+
+    virtual void NotifyContentChanged();
+
+    const std::string& GetName() const
+    {
+      return name_;
+    }
+
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Widgets/WorldSceneWidget.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,231 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "WorldSceneWidget.h"
+
+#include "PanMouseTracker.h"
+#include "ZoomMouseTracker.h"
+#include "PanZoomMouseTracker.h"
+
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+
+#include <math.h>
+#include <memory>
+#include <cassert>
+
+namespace Deprecated
+{
+  // this is an adapter between a IWorldSceneMouseTracker
+  // that is tracking a mouse in scene coordinates/mm and
+  // an IMouseTracker that is tracking a mouse
+  // in screen coordinates/pixels.
+  class WorldSceneWidget::SceneMouseTracker : public IMouseTracker
+  {
+  private:
+    ViewportGeometry                        view_;
+    std::auto_ptr<IWorldSceneMouseTracker>  tracker_;
+
+  public:
+    SceneMouseTracker(const ViewportGeometry& view,
+                      IWorldSceneMouseTracker* tracker) :
+      view_(view),
+      tracker_(tracker)
+    {
+      if (tracker == NULL)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+      }
+    }
+
+    virtual void Render(Orthanc::ImageAccessor& target)
+    {
+      if (tracker_->HasRender())
+      {
+        OrthancStone::CairoSurface surface(target, false /* no alpha */);
+        OrthancStone::CairoContext context(surface);
+        view_.ApplyTransform(context);
+        tracker_->Render(context, view_.GetZoom());
+      }
+    }
+
+    virtual void MouseUp()
+    {
+      tracker_->MouseUp();
+    }
+
+    virtual void MouseMove(int x,
+                           int y,
+                           const std::vector<Touch>& displayTouches)
+    {
+      double sceneX, sceneY;
+      view_.MapPixelCenterToScene(sceneX, sceneY, x, y);
+
+      std::vector<Touch> sceneTouches;
+      for (size_t t = 0; t < displayTouches.size(); t++)
+      {
+        double sx, sy;
+        
+        view_.MapPixelCenterToScene(
+          sx, sy, (int)displayTouches[t].x, (int)displayTouches[t].y);
+        
+        sceneTouches.push_back(
+          Touch(static_cast<float>(sx), static_cast<float>(sy)));
+      }
+      tracker_->MouseMove(x, y, sceneX, sceneY, displayTouches, sceneTouches);
+    }
+  };
+
+
+  bool WorldSceneWidget::RenderCairo(OrthancStone::CairoContext& context)
+  {
+    view_.ApplyTransform(context);
+    return RenderScene(context, view_);
+  }
+
+
+  void WorldSceneWidget::RenderMouseOverCairo(OrthancStone::CairoContext& context,
+                                              int x,
+                                              int y)
+  {
+    ViewportGeometry view = GetView();
+    view.ApplyTransform(context);
+
+    double sceneX, sceneY;
+    view.MapPixelCenterToScene(sceneX, sceneY, x, y);
+
+    if (interactor_)
+    {
+      interactor_->MouseOver(context, *this, view, sceneX, sceneY, GetStatusBar());
+    }
+  }
+
+
+  void WorldSceneWidget::SetSceneExtent(ViewportGeometry& view)
+  {
+    view.SetSceneExtent(GetSceneExtent());
+  }
+
+
+  void WorldSceneWidget::SetSize(unsigned int width,
+                                 unsigned int height)
+  {
+    CairoWidget::SetSize(width, height);
+    view_.SetDisplaySize(width, height);
+  }
+
+
+  void WorldSceneWidget::SetInteractor(IWorldSceneInteractor& interactor)
+  {
+    interactor_ = &interactor;
+  }
+
+
+  void WorldSceneWidget::FitContent()
+  {
+    SetSceneExtent(view_);
+    view_.FitContent();
+
+    NotifyContentChanged();
+  }
+
+
+  void WorldSceneWidget::SetView(const ViewportGeometry& view)
+  {
+    view_ = view;
+
+    NotifyContentChanged();
+  }
+
+
+  IMouseTracker* WorldSceneWidget::CreateMouseTracker(OrthancStone::MouseButton button,
+                                                      int x,
+                                                      int y,
+                                                      OrthancStone::KeyboardModifiers modifiers,
+                                                      const std::vector<Touch>& touches)
+  {
+    double sceneX, sceneY;
+    view_.MapPixelCenterToScene(sceneX, sceneY, x, y);
+
+    // asks the Widget Interactor to provide a mouse tracker
+    std::auto_ptr<IWorldSceneMouseTracker> tracker;
+
+    if (interactor_)
+    {
+      tracker.reset(interactor_->CreateMouseTracker(*this, view_, button, modifiers, x, y, sceneX, sceneY, GetStatusBar(), touches));
+    }
+    
+    if (tracker.get() != NULL)
+    {
+      return new SceneMouseTracker(view_, tracker.release());
+    }
+    else if (hasDefaultMouseEvents_)
+    {
+      printf("has default mouse events\n");
+      if (touches.size() == 2)
+      {
+        printf("2 touches !\n");
+        return new SceneMouseTracker(view_, new PanZoomMouseTracker(*this, touches));
+      }
+      else
+      {
+        switch (button)
+        {
+          case OrthancStone::MouseButton_Middle:
+            return new SceneMouseTracker(view_, new PanMouseTracker(*this, x, y));
+
+          case OrthancStone::MouseButton_Right:
+            return new SceneMouseTracker(view_, new ZoomMouseTracker(*this, x, y));
+
+          default:
+            return NULL;
+        }
+      }
+    }
+    else
+    {
+      return NULL;
+    }
+  }
+
+
+  void WorldSceneWidget::MouseWheel(OrthancStone::MouseWheelDirection direction,
+                                    int x,
+                                    int y,
+                                    OrthancStone::KeyboardModifiers modifiers)
+  {
+    if (interactor_)
+    {
+      interactor_->MouseWheel(*this, direction, modifiers, GetStatusBar());
+    }
+  }
+
+
+  void WorldSceneWidget::KeyPressed(OrthancStone::KeyboardKeys key,
+                                    char keyChar,
+                                    OrthancStone::KeyboardModifiers modifiers)
+  {
+    if (interactor_)
+    {
+      interactor_->KeyPressed(*this, key, keyChar, modifiers, GetStatusBar());
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Widgets/WorldSceneWidget.h	Wed May 22 16:13:46 2019 +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-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "CairoWidget.h"
+#include "IWorldSceneInteractor.h"
+
+#include "../Toolbox/ViewportGeometry.h"
+
+namespace Deprecated
+{
+  class WorldSceneWidget : public CairoWidget
+  {
+  private:
+    class SceneMouseTracker;
+
+    ViewportGeometry       view_;
+    IWorldSceneInteractor* interactor_;
+    bool                   hasDefaultMouseEvents_;
+
+  protected:
+    virtual OrthancStone::Extent2D GetSceneExtent() = 0;
+
+    virtual bool RenderScene(OrthancStone::CairoContext& context,
+                             const ViewportGeometry& view) = 0;
+
+    // From CairoWidget
+    virtual bool RenderCairo(OrthancStone::CairoContext& context);
+
+    // From CairoWidget
+    virtual void RenderMouseOverCairo(OrthancStone::CairoContext& context,
+                                      int x,
+                                      int y);
+
+    void SetSceneExtent(ViewportGeometry& geometry);
+
+  public:
+    WorldSceneWidget(const std::string& name) :
+      CairoWidget(name),
+      interactor_(NULL),
+      hasDefaultMouseEvents_(true)
+    {
+    }
+
+    void SetDefaultMouseEvents(bool value)
+    {
+      hasDefaultMouseEvents_ = value;
+    }
+
+    bool HasDefaultMouseEvents() const
+    {
+      return hasDefaultMouseEvents_;
+    }
+
+    void SetInteractor(IWorldSceneInteractor& interactor);
+
+    void SetView(const ViewportGeometry& view);
+
+    const ViewportGeometry& GetView() const
+    {
+      return view_;
+    }
+
+    virtual void SetSize(unsigned int width,
+                         unsigned int height);
+
+    virtual void FitContent();
+
+    virtual IMouseTracker* CreateMouseTracker(OrthancStone::MouseButton button,
+                                              int x,
+                                              int y,
+                                              OrthancStone::KeyboardModifiers modifiers,
+                                              const std::vector<Touch>& touches);
+
+    virtual void MouseWheel(OrthancStone::MouseWheelDirection direction,
+                            int x,
+                            int y,
+                            OrthancStone::KeyboardModifiers modifiers);
+
+    virtual void KeyPressed(OrthancStone::KeyboardKeys key,
+                            char keyChar,
+                            OrthancStone::KeyboardModifiers modifiers);
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Widgets/ZoomMouseTracker.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,110 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "ZoomMouseTracker.h"
+
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+
+namespace Deprecated
+{
+  ZoomMouseTracker::ZoomMouseTracker(WorldSceneWidget& that,
+                                     int x,
+                                     int y) :
+    that_(that),
+    originalZoom_(that.GetView().GetZoom()),
+    downX_(x),
+    downY_(y)
+  {
+    that.GetView().MapPixelCenterToScene(centerX_, centerY_, x, y);
+
+    unsigned int height = that.GetView().GetDisplayHeight();
+      
+    if (height <= 3)
+    {
+      idle_ = true;
+      LOG(WARNING) << "image is too small to zoom (current height = " << height << ")";
+    }
+    else
+    {
+      idle_ = false;
+      normalization_ = 1.0 / static_cast<double>(height - 1);
+    }
+  }
+    
+
+  void ZoomMouseTracker::Render(OrthancStone::CairoContext& context,
+                                double zoom)
+  {
+    throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+  }
+
+
+  void ZoomMouseTracker::MouseMove(int displayX,
+                                   int displayY,
+                                   double x,
+                                   double y,
+                                   const std::vector<Touch>& displayTouches,
+                                   const std::vector<Touch>& sceneTouches)
+  {
+    static const double MIN_ZOOM = -4;
+    static const double MAX_ZOOM = 4;
+
+      
+    if (!idle_)
+    {
+      double dy = static_cast<double>(displayY - downY_) * normalization_;  // In the range [-1,1]
+      double z;
+
+      // Linear interpolation from [-1, 1] to [MIN_ZOOM, MAX_ZOOM]
+      if (dy < -1.0)
+      {
+        z = MIN_ZOOM;
+      }
+      else if (dy > 1.0)
+      {
+        z = MAX_ZOOM;
+      }
+      else
+      {
+        z = MIN_ZOOM + (MAX_ZOOM - MIN_ZOOM) * (dy + 1.0) / 2.0;
+      }
+
+      z = pow(2.0, z);
+
+      ViewportGeometry view = that_.GetView();
+        
+      view.SetZoom(z * originalZoom_);
+        
+      // Correct the pan so that the original click point is kept at
+      // the same location on the display
+      double panX, panY;
+      view.GetPan(panX, panY);
+
+      int tx, ty;
+      view.MapSceneToDisplay(tx, ty, centerX_, centerY_);
+      view.SetPan(panX + static_cast<double>(downX_ - tx),
+                  panY + static_cast<double>(downY_ - ty));
+        
+      that_.SetView(view);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/Widgets/ZoomMouseTracker.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,64 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "WorldSceneWidget.h"
+
+namespace Deprecated
+{
+  class ZoomMouseTracker : public IWorldSceneMouseTracker
+  {
+  private:
+    WorldSceneWidget&  that_;
+    double             originalZoom_;
+    int                downX_;
+    int                downY_;
+    double             centerX_;
+    double             centerY_;
+    bool               idle_;
+    double             normalization_;
+    
+  public:
+    ZoomMouseTracker(WorldSceneWidget& that,
+                     int x,
+                     int y);
+    
+    virtual bool HasRender() const
+    {
+      return false;
+    }
+
+    virtual void MouseUp()
+    {
+    }
+
+    virtual void Render(OrthancStone::CairoContext& context,
+                        double zoom);
+
+    virtual void MouseMove(int displayX,
+                           int displayY,
+                           double x,
+                           double y,
+                           const std::vector<Touch>& displayTouches,
+                           const std::vector<Touch>& sceneTouches);
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Deprecated/dev.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,958 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "Layers/FrameRenderer.h"
+#include "Layers/LineLayerRenderer.h"
+#include "Layers/SliceOutlineRenderer.h"
+#include "Toolbox/DownloadStack.h"
+#include "Toolbox/GeometryToolbox.h"
+#include "Toolbox/OrthancSlicesLoader.h"
+#include "Volumes/ISlicedVolume.h"
+#include "Volumes/ImageBuffer3D.h"
+#include "Widgets/SliceViewerWidget.h"
+
+#include <Core/Logging.h>
+#include <Core/Images/ImageProcessing.h>
+#include <Core/OrthancException.h>
+
+#include <boost/math/special_functions/round.hpp>
+
+
+namespace Deprecated
+{
+  // TODO: Handle errors while loading
+  class OrthancVolumeImage :
+    public ISlicedVolume,
+    public OrthancStone::IObserver
+  {
+  private:
+    OrthancSlicesLoader           loader_;
+    std::auto_ptr<OrthancStone::ImageBuffer3D>  image_;
+    std::auto_ptr<DownloadStack>  downloadStack_;
+    bool                          computeRange_;
+    size_t                        pendingSlices_;
+
+    void ScheduleSliceDownload()
+    {
+      assert(downloadStack_.get() != NULL);
+
+      unsigned int slice;
+      if (downloadStack_->Pop(slice))
+      {
+        loader_.ScheduleLoadSliceImage(slice, OrthancStone::SliceImageQuality_Jpeg90);
+      }
+    }
+
+
+    static bool IsCompatible(const Slice& a,
+                             const Slice& b)
+    {
+      if (!OrthancStone::GeometryToolbox::IsParallel(a.GetGeometry().GetNormal(),
+                                                     b.GetGeometry().GetNormal()))
+      {
+        LOG(ERROR) << "A slice in the volume image is not parallel to the others.";
+        return false;
+      }
+
+      if (a.GetConverter().GetExpectedPixelFormat() != b.GetConverter().GetExpectedPixelFormat())
+      {
+        LOG(ERROR) << "The pixel format changes across the slices of the volume image.";
+        return false;
+      }
+
+      if (a.GetWidth() != b.GetWidth() ||
+          a.GetHeight() != b.GetHeight())
+      {
+        LOG(ERROR) << "The slices dimensions (width/height) are varying throughout the volume image";
+        return false;
+      }
+
+      if (!OrthancStone::LinearAlgebra::IsNear(a.GetPixelSpacingX(), b.GetPixelSpacingX()) ||
+          !OrthancStone::LinearAlgebra::IsNear(a.GetPixelSpacingY(), b.GetPixelSpacingY()))
+      {
+        LOG(ERROR) << "The pixel spacing of the slices change across the volume image";
+        return false;
+      }
+
+      return true;
+    }
+
+
+    static double GetDistance(const Slice& a,
+                              const Slice& b)
+    {
+      return fabs(a.GetGeometry().ProjectAlongNormal(a.GetGeometry().GetOrigin()) -
+                  a.GetGeometry().ProjectAlongNormal(b.GetGeometry().GetOrigin()));
+    }
+
+
+    void OnSliceGeometryReady(const OrthancSlicesLoader::SliceGeometryReadyMessage& message)
+    {
+      assert(&message.GetOrigin() == &loader_);
+
+      if (loader_.GetSlicesCount() == 0)
+      {
+        LOG(ERROR) << "Empty volume image";
+        BroadcastMessage(ISlicedVolume::GeometryErrorMessage(*this));
+        return;
+      }
+
+      for (size_t i = 1; i < loader_.GetSlicesCount(); i++)
+      {
+        if (!IsCompatible(loader_.GetSlice(0), loader_.GetSlice(i)))
+        {
+          BroadcastMessage(ISlicedVolume::GeometryErrorMessage(*this));
+          return;
+        }
+      }
+
+      double spacingZ;
+
+      if (loader_.GetSlicesCount() > 1)
+      {
+        spacingZ = GetDistance(loader_.GetSlice(0), loader_.GetSlice(1));
+      }
+      else
+      {
+        // This is a volume with one single slice: Choose a dummy
+        // z-dimension for voxels
+        spacingZ = 1;
+      }
+
+      for (size_t i = 1; i < loader_.GetSlicesCount(); i++)
+      {
+        if (!OrthancStone::LinearAlgebra::IsNear(spacingZ, GetDistance(loader_.GetSlice(i - 1), loader_.GetSlice(i)),
+                                                 0.001 /* this is expressed in mm */))
+        {
+          LOG(ERROR) << "The distance between successive slices is not constant in a volume image";
+          BroadcastMessage(ISlicedVolume::GeometryErrorMessage(*this));
+          return;
+        }
+      }
+
+      unsigned int width = loader_.GetSlice(0).GetWidth();
+      unsigned int height = loader_.GetSlice(0).GetHeight();
+      Orthanc::PixelFormat format = loader_.GetSlice(0).GetConverter().GetExpectedPixelFormat();
+      LOG(INFO) << "Creating a volume image of size " << width << "x" << height
+                << "x" << loader_.GetSlicesCount() << " in " << Orthanc::EnumerationToString(format);
+
+      image_.reset(new OrthancStone::ImageBuffer3D(format, width, height, static_cast<unsigned int>(loader_.GetSlicesCount()), computeRange_));
+      image_->GetGeometry().SetAxialGeometry(loader_.GetSlice(0).GetGeometry());
+      image_->GetGeometry().SetVoxelDimensions(loader_.GetSlice(0).GetPixelSpacingX(),
+                                               loader_.GetSlice(0).GetPixelSpacingY(), spacingZ);
+      image_->Clear();
+
+      downloadStack_.reset(new DownloadStack(static_cast<unsigned int>(loader_.GetSlicesCount())));
+      pendingSlices_ = loader_.GetSlicesCount();
+
+      for (unsigned int i = 0; i < 4; i++)  // Limit to 4 simultaneous downloads
+      {
+        ScheduleSliceDownload();
+      }
+
+      // TODO Check the DicomFrameConverter are constant
+
+      BroadcastMessage(ISlicedVolume::GeometryReadyMessage(*this));
+    }
+
+
+    void OnSliceGeometryError(const OrthancSlicesLoader::SliceGeometryErrorMessage& message)
+    {
+      assert(&message.GetOrigin() == &loader_);
+
+      LOG(ERROR) << "Unable to download a volume image";
+      BroadcastMessage(ISlicedVolume::GeometryErrorMessage(*this));
+    }
+
+
+    void OnSliceImageReady(const OrthancSlicesLoader::SliceImageReadyMessage& message)
+    {
+      assert(&message.GetOrigin() == &loader_);
+
+      {
+        OrthancStone::ImageBuffer3D::SliceWriter writer(*image_, OrthancStone::VolumeProjection_Axial, message.GetSliceIndex());
+        Orthanc::ImageProcessing::Copy(writer.GetAccessor(), message.GetImage());
+      }
+
+      BroadcastMessage(ISlicedVolume::SliceContentChangedMessage
+                       (*this, message.GetSliceIndex(), message.GetSlice()));
+
+      if (pendingSlices_ == 1)
+      {
+        BroadcastMessage(ISlicedVolume::VolumeReadyMessage(*this));
+        pendingSlices_ = 0;
+      }
+      else if (pendingSlices_ > 1)
+      {
+        pendingSlices_ -= 1;
+      }
+
+      ScheduleSliceDownload();
+    }
+
+
+    void OnSliceImageError(const OrthancSlicesLoader::SliceImageErrorMessage& message)
+    {
+      assert(&message.GetOrigin() == &loader_);
+
+      LOG(ERROR) << "Cannot download slice " << message.GetSliceIndex() << " in a volume image";
+      ScheduleSliceDownload();
+    }
+
+
+  public:
+    OrthancVolumeImage(OrthancStone::MessageBroker& broker,
+                       OrthancApiClient& orthanc,
+                       bool computeRange) :
+      ISlicedVolume(broker),
+      IObserver(broker),
+      loader_(broker, orthanc),
+      computeRange_(computeRange),
+      pendingSlices_(0)
+    {
+      loader_.RegisterObserverCallback(
+        new OrthancStone::Callable<OrthancVolumeImage, OrthancSlicesLoader::SliceGeometryReadyMessage>
+        (*this, &OrthancVolumeImage::OnSliceGeometryReady));
+
+      loader_.RegisterObserverCallback(
+        new OrthancStone::Callable<OrthancVolumeImage, OrthancSlicesLoader::SliceGeometryErrorMessage>
+        (*this, &OrthancVolumeImage::OnSliceGeometryError));
+
+      loader_.RegisterObserverCallback(
+        new OrthancStone::Callable<OrthancVolumeImage, OrthancSlicesLoader::SliceImageReadyMessage>
+        (*this, &OrthancVolumeImage::OnSliceImageReady));
+
+      loader_.RegisterObserverCallback(
+        new OrthancStone::Callable<OrthancVolumeImage, OrthancSlicesLoader::SliceImageErrorMessage>
+        (*this, &OrthancVolumeImage::OnSliceImageError));
+    }
+
+    void ScheduleLoadSeries(const std::string& seriesId)
+    {
+      loader_.ScheduleLoadSeries(seriesId);
+    }
+
+    void ScheduleLoadInstance(const std::string& instanceId)
+    {
+      loader_.ScheduleLoadInstance(instanceId);
+    }
+
+    void ScheduleLoadFrame(const std::string& instanceId,
+                           unsigned int frame)
+    {
+      loader_.ScheduleLoadFrame(instanceId, frame);
+    }
+
+    virtual size_t GetSlicesCount() const
+    {
+      return loader_.GetSlicesCount();
+    }
+
+    virtual const Slice& GetSlice(size_t index) const
+    {
+      return loader_.GetSlice(index);
+    }
+
+    OrthancStone::ImageBuffer3D& GetImage() const
+    {
+      if (image_.get() == NULL)
+      {
+        // The geometry is not ready yet
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+      }
+      else
+      {
+        return *image_;
+      }
+    }
+
+    bool FitWindowingToRange(RenderStyle& style,
+                             const DicomFrameConverter& converter) const
+    {
+      if (image_.get() == NULL)
+      {
+        return false;
+      }
+      else
+      {
+        return image_->FitWindowingToRange(style, converter);
+      }
+    }
+  };
+
+
+  class VolumeImageGeometry
+  {
+  private:
+    unsigned int         width_;
+    unsigned int         height_;
+    size_t               depth_;
+    double               pixelSpacingX_;
+    double               pixelSpacingY_;
+    double               sliceThickness_;
+    OrthancStone::CoordinateSystem3D   reference_;
+    DicomFrameConverter  converter_;
+
+    double ComputeAxialThickness(const OrthancVolumeImage& volume) const
+    {
+      double thickness;
+
+      size_t n = volume.GetSlicesCount();
+      if (n > 1)
+      {
+        const Slice& a = volume.GetSlice(0);
+        const Slice& b = volume.GetSlice(n - 1);
+        thickness = ((reference_.ProjectAlongNormal(b.GetGeometry().GetOrigin()) -
+                      reference_.ProjectAlongNormal(a.GetGeometry().GetOrigin())) /
+                     (static_cast<double>(n) - 1.0));
+      }
+      else
+      {
+        thickness = volume.GetSlice(0).GetThickness();
+      }
+
+      if (thickness <= 0)
+      {
+        // The slices should have been sorted with increasing Z
+        // (along the normal) by the OrthancSlicesLoader
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+      }
+      else
+      {
+        return thickness;
+      }
+    }
+
+    void SetupAxial(const OrthancVolumeImage& volume)
+    {
+      const Slice& axial = volume.GetSlice(0);
+
+      width_ = axial.GetWidth();
+      height_ = axial.GetHeight();
+      depth_ = volume.GetSlicesCount();
+
+      pixelSpacingX_ = axial.GetPixelSpacingX();
+      pixelSpacingY_ = axial.GetPixelSpacingY();
+      sliceThickness_ = ComputeAxialThickness(volume);
+
+      reference_ = axial.GetGeometry();
+    }
+
+    void SetupCoronal(const OrthancVolumeImage& volume)
+    {
+      const Slice& axial = volume.GetSlice(0);
+      double axialThickness = ComputeAxialThickness(volume);
+
+      width_ = axial.GetWidth();
+      height_ = static_cast<unsigned int>(volume.GetSlicesCount());
+      depth_ = axial.GetHeight();
+
+      pixelSpacingX_ = axial.GetPixelSpacingX();
+      pixelSpacingY_ = axialThickness;
+      sliceThickness_ = axial.GetPixelSpacingY();
+
+      OrthancStone::Vector origin = axial.GetGeometry().GetOrigin();
+      origin += (static_cast<double>(volume.GetSlicesCount() - 1) *
+                 axialThickness * axial.GetGeometry().GetNormal());
+
+      reference_ = OrthancStone::CoordinateSystem3D(origin,
+                                                    axial.GetGeometry().GetAxisX(),
+                                                    - axial.GetGeometry().GetNormal());
+    }
+
+    void SetupSagittal(const OrthancVolumeImage& volume)
+    {
+      const Slice& axial = volume.GetSlice(0);
+      double axialThickness = ComputeAxialThickness(volume);
+
+      width_ = axial.GetHeight();
+      height_ = static_cast<unsigned int>(volume.GetSlicesCount());
+      depth_ = axial.GetWidth();
+
+      pixelSpacingX_ = axial.GetPixelSpacingY();
+      pixelSpacingY_ = axialThickness;
+      sliceThickness_ = axial.GetPixelSpacingX();
+
+      OrthancStone::Vector origin = axial.GetGeometry().GetOrigin();
+      origin += (static_cast<double>(volume.GetSlicesCount() - 1) *
+                 axialThickness * axial.GetGeometry().GetNormal());
+
+      reference_ = OrthancStone::CoordinateSystem3D(origin,
+                                                    axial.GetGeometry().GetAxisY(),
+                                                    axial.GetGeometry().GetNormal());
+    }
+
+  public:
+    VolumeImageGeometry(const OrthancVolumeImage& volume,
+                        OrthancStone::VolumeProjection projection)
+    {
+      if (volume.GetSlicesCount() == 0)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+      }
+
+      converter_ = volume.GetSlice(0).GetConverter();
+
+      switch (projection)
+      {
+        case OrthancStone::VolumeProjection_Axial:
+          SetupAxial(volume);
+          break;
+
+        case OrthancStone::VolumeProjection_Coronal:
+          SetupCoronal(volume);
+          break;
+
+        case OrthancStone::VolumeProjection_Sagittal:
+          SetupSagittal(volume);
+          break;
+
+        default:
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+      }
+    }
+
+    size_t GetSlicesCount() const
+    {
+      return depth_;
+    }
+
+    const OrthancStone::Vector& GetNormal() const
+    {
+      return reference_.GetNormal();
+    }
+
+    bool LookupSlice(size_t& index,
+                     const OrthancStone::CoordinateSystem3D& slice) const
+    {
+      bool opposite;
+      if (!OrthancStone::GeometryToolbox::IsParallelOrOpposite(opposite,
+                                                               reference_.GetNormal(),
+                                                               slice.GetNormal()))
+      {
+        return false;
+      }
+
+      double z = (reference_.ProjectAlongNormal(slice.GetOrigin()) -
+                  reference_.ProjectAlongNormal(reference_.GetOrigin())) / sliceThickness_;
+
+      int s = static_cast<int>(boost::math::iround(z));
+
+      if (s < 0 ||
+          s >= static_cast<int>(depth_))
+      {
+        return false;
+      }
+      else
+      {
+        index = static_cast<size_t>(s);
+        return true;
+      }
+    }
+
+    Slice* GetSlice(size_t slice) const
+    {
+      if (slice >= depth_)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+      }
+      else
+      {
+        OrthancStone::CoordinateSystem3D origin(reference_.GetOrigin() +
+                                                static_cast<double>(slice) * sliceThickness_ * reference_.GetNormal(),
+                                                reference_.GetAxisX(),
+                                                reference_.GetAxisY());
+
+        return new Slice(origin, pixelSpacingX_, pixelSpacingY_, sliceThickness_,
+                         width_, height_, converter_);
+      }
+    }
+  };
+
+
+
+  class VolumeImageMPRSlicer :
+    public IVolumeSlicer,
+    public OrthancStone::IObserver
+  {
+  private:
+    class RendererFactory : public LayerReadyMessage::IRendererFactory
+    {
+    private:
+      const Orthanc::ImageAccessor&  frame_;
+      const Slice&                   slice_;
+      bool                           isFullQuality_;
+
+    public:
+      RendererFactory(const Orthanc::ImageAccessor& frame,
+                      const Slice& slice,
+                      bool isFullQuality) :
+        frame_(frame),
+        slice_(slice),
+        isFullQuality_(isFullQuality)
+      {
+      }
+
+      virtual ILayerRenderer* CreateRenderer() const
+      {
+        return FrameRenderer::CreateRenderer(frame_, slice_, isFullQuality_);
+      }
+    };
+
+
+    OrthancVolumeImage&                 volume_;
+    std::auto_ptr<VolumeImageGeometry>  axialGeometry_;
+    std::auto_ptr<VolumeImageGeometry>  coronalGeometry_;
+    std::auto_ptr<VolumeImageGeometry>  sagittalGeometry_;
+
+
+    bool IsGeometryReady() const
+    {
+      return axialGeometry_.get() != NULL;
+    }
+
+    void OnGeometryReady(const ISlicedVolume::GeometryReadyMessage& message)
+    {
+      assert(&message.GetOrigin() == &volume_);
+
+      // These 3 values are only used to speed up the IVolumeSlicer
+      axialGeometry_.reset(new VolumeImageGeometry(volume_, OrthancStone::VolumeProjection_Axial));
+      coronalGeometry_.reset(new VolumeImageGeometry(volume_, OrthancStone::VolumeProjection_Coronal));
+      sagittalGeometry_.reset(new VolumeImageGeometry(volume_, OrthancStone::VolumeProjection_Sagittal));
+
+      BroadcastMessage(IVolumeSlicer::GeometryReadyMessage(*this));
+    }
+
+    void OnGeometryError(const ISlicedVolume::GeometryErrorMessage& message)
+    {
+      assert(&message.GetOrigin() == &volume_);
+
+      BroadcastMessage(IVolumeSlicer::GeometryErrorMessage(*this));
+    }
+
+    void OnContentChanged(const ISlicedVolume::ContentChangedMessage& message)
+    {
+      assert(&message.GetOrigin() == &volume_);
+
+      BroadcastMessage(IVolumeSlicer::ContentChangedMessage(*this));
+    }
+
+    void OnSliceContentChanged(const ISlicedVolume::SliceContentChangedMessage& message)
+    {
+      assert(&message.GetOrigin() == &volume_);
+
+      //IVolumeSlicer::OnSliceContentChange(slice);
+
+      // TODO Improve this?
+      BroadcastMessage(IVolumeSlicer::ContentChangedMessage(*this));
+    }
+
+    const VolumeImageGeometry& GetProjectionGeometry(OrthancStone::VolumeProjection projection)
+    {
+      if (!IsGeometryReady())
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+      }
+
+      switch (projection)
+      {
+        case OrthancStone::VolumeProjection_Axial:
+          return *axialGeometry_;
+
+        case OrthancStone::VolumeProjection_Sagittal:
+          return *sagittalGeometry_;
+
+        case OrthancStone::VolumeProjection_Coronal:
+          return *coronalGeometry_;
+
+        default:
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      }
+    }
+
+
+    bool DetectProjection(OrthancStone::VolumeProjection& projection,
+                          const OrthancStone::CoordinateSystem3D& viewportSlice)
+    {
+      bool isOpposite;  // Ignored
+
+      if (OrthancStone::GeometryToolbox::IsParallelOrOpposite(isOpposite,
+                                                              viewportSlice.GetNormal(),
+                                                              axialGeometry_->GetNormal()))
+      {
+        projection = OrthancStone::VolumeProjection_Axial;
+        return true;
+      }
+      else if (OrthancStone::GeometryToolbox::IsParallelOrOpposite(isOpposite,
+                                                                   viewportSlice.GetNormal(),
+                                                                   sagittalGeometry_->GetNormal()))
+      {
+        projection = OrthancStone::VolumeProjection_Sagittal;
+        return true;
+      }
+      else if (OrthancStone::GeometryToolbox::IsParallelOrOpposite(isOpposite,
+                                                                   viewportSlice.GetNormal(),
+                                                                   coronalGeometry_->GetNormal()))
+      {
+        projection = OrthancStone::VolumeProjection_Coronal;
+        return true;
+      }
+      else
+      {
+        return false;
+      }
+    }
+
+
+  public:
+    VolumeImageMPRSlicer(OrthancStone::MessageBroker& broker,
+                         OrthancVolumeImage&  volume) :
+      IVolumeSlicer(broker),
+      IObserver(broker),
+      volume_(volume)
+    {
+      volume_.RegisterObserverCallback(
+        new OrthancStone::Callable<VolumeImageMPRSlicer, ISlicedVolume::GeometryReadyMessage>
+        (*this, &VolumeImageMPRSlicer::OnGeometryReady));
+
+      volume_.RegisterObserverCallback(
+        new OrthancStone::Callable<VolumeImageMPRSlicer, ISlicedVolume::GeometryErrorMessage>
+        (*this, &VolumeImageMPRSlicer::OnGeometryError));
+
+      volume_.RegisterObserverCallback(
+        new OrthancStone::Callable<VolumeImageMPRSlicer, ISlicedVolume::ContentChangedMessage>
+        (*this, &VolumeImageMPRSlicer::OnContentChanged));
+
+      volume_.RegisterObserverCallback(
+        new OrthancStone::Callable<VolumeImageMPRSlicer, ISlicedVolume::SliceContentChangedMessage>
+        (*this, &VolumeImageMPRSlicer::OnSliceContentChanged));
+    }
+
+    virtual bool GetExtent(std::vector<OrthancStone::Vector>& points,
+                           const OrthancStone::CoordinateSystem3D& viewportSlice) ORTHANC_OVERRIDE
+    {
+      OrthancStone::VolumeProjection projection;
+
+      if (!IsGeometryReady() ||
+          !DetectProjection(projection, viewportSlice))
+      {
+        return false;
+      }
+      else
+      {
+        // As the slices of the volumic image are arranged in a box,
+        // we only consider one single reference slice (the one with index 0).
+        std::auto_ptr<Slice> slice(GetProjectionGeometry(projection).GetSlice(0));
+        slice->GetExtent(points);
+
+        return true;
+      }
+    }
+
+    virtual void ScheduleLayerCreation(const OrthancStone::CoordinateSystem3D& viewportSlice) ORTHANC_OVERRIDE
+    {
+      OrthancStone::VolumeProjection projection;
+
+      if (IsGeometryReady() &&
+          DetectProjection(projection, viewportSlice))
+      {
+        const VolumeImageGeometry& geometry = GetProjectionGeometry(projection);
+
+        size_t closest;
+
+        if (geometry.LookupSlice(closest, viewportSlice))
+        {
+          bool isFullQuality = true;  // TODO
+
+          std::auto_ptr<Orthanc::Image> frame;
+
+          {
+            OrthancStone::ImageBuffer3D::SliceReader reader(volume_.GetImage(), projection, static_cast<unsigned int>(closest));
+
+            // TODO Transfer ownership if non-axial, to avoid memcpy
+            frame.reset(Orthanc::Image::Clone(reader.GetAccessor()));
+          }
+
+          std::auto_ptr<Slice> slice(geometry.GetSlice(closest));
+
+          RendererFactory factory(*frame, *slice, isFullQuality);
+
+          BroadcastMessage(IVolumeSlicer::LayerReadyMessage(*this, factory, slice->GetGeometry()));
+          return;
+        }
+      }
+
+      // Error
+      OrthancStone::CoordinateSystem3D slice;
+      BroadcastMessage(IVolumeSlicer::LayerErrorMessage(*this, slice));
+    }
+  };
+
+
+  class VolumeImageInteractor :
+    public IWorldSceneInteractor,
+    public OrthancStone::IObserver
+  {
+  private:
+    SliceViewerWidget&                  widget_;
+    OrthancStone::VolumeProjection      projection_;
+    std::auto_ptr<VolumeImageGeometry>  slices_;
+    size_t                              slice_;
+
+  protected:
+    void OnGeometryReady(const ISlicedVolume::GeometryReadyMessage& message)
+    {
+      if (slices_.get() == NULL)
+      {
+        const OrthancVolumeImage& image =
+          dynamic_cast<const OrthancVolumeImage&>(message.GetOrigin());
+
+        slices_.reset(new VolumeImageGeometry(image, projection_));
+        SetSlice(slices_->GetSlicesCount() / 2);
+
+        widget_.FitContent();
+      }
+    }
+
+    virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget,
+                                                        const ViewportGeometry& view,
+                                                        OrthancStone::MouseButton button,
+                                                        OrthancStone::KeyboardModifiers modifiers,
+                                                        int viewportX,
+                                                        int viewportY,
+                                                        double x,
+                                                        double y,
+                                                        IStatusBar* statusBar,
+                                                        const std::vector<Touch>& touches) ORTHANC_OVERRIDE
+    {
+      return  NULL;
+    }
+
+    virtual void MouseOver(OrthancStone::CairoContext& context,
+                           WorldSceneWidget& widget,
+                           const ViewportGeometry& view,
+                           double x,
+                           double y,
+                           IStatusBar* statusBar) ORTHANC_OVERRIDE
+    {
+    }
+
+    virtual void MouseWheel(WorldSceneWidget& widget,
+                            OrthancStone::MouseWheelDirection direction,
+                            OrthancStone::KeyboardModifiers modifiers,
+                            IStatusBar* statusBar) ORTHANC_OVERRIDE
+    {
+      int scale = (modifiers & OrthancStone::KeyboardModifiers_Control ? 10 : 1);
+
+      switch (direction)
+      {
+        case OrthancStone::MouseWheelDirection_Up:
+          OffsetSlice(-scale);
+          break;
+
+        case OrthancStone::MouseWheelDirection_Down:
+          OffsetSlice(scale);
+          break;
+
+        default:
+          break;
+      }
+    }
+
+    virtual void KeyPressed(WorldSceneWidget& widget,
+                            OrthancStone::KeyboardKeys key,
+                            char keyChar,
+                            OrthancStone::KeyboardModifiers modifiers,
+                            IStatusBar* statusBar) ORTHANC_OVERRIDE
+    {
+      switch (keyChar)
+      {
+        case 's':
+          widget.FitContent();
+          break;
+
+        default:
+          break;
+      }
+    }
+
+  public:
+    VolumeImageInteractor(OrthancStone::MessageBroker& broker,
+                          OrthancVolumeImage& volume,
+                          SliceViewerWidget& widget,
+                          OrthancStone::VolumeProjection projection) :
+      IObserver(broker),
+      widget_(widget),
+      projection_(projection)
+    {
+      widget.SetInteractor(*this);
+
+      volume.RegisterObserverCallback(
+        new OrthancStone::Callable<VolumeImageInteractor, ISlicedVolume::GeometryReadyMessage>
+        (*this, &VolumeImageInteractor::OnGeometryReady));
+    }
+
+    bool IsGeometryReady() const
+    {
+      return slices_.get() != NULL;
+    }
+
+    size_t GetSlicesCount() const
+    {
+      if (slices_.get() == NULL)
+      {
+        return 0;
+      }
+      else
+      {
+        return slices_->GetSlicesCount();
+      }
+    }
+
+    void OffsetSlice(int offset)
+    {
+      if (slices_.get() != NULL)
+      {
+        int slice = static_cast<int>(slice_) + offset;
+
+        if (slice < 0)
+        {
+          slice = 0;
+        }
+
+        if (slice >= static_cast<int>(slices_->GetSlicesCount()))
+        {
+          slice = static_cast<unsigned int>(slices_->GetSlicesCount()) - 1;
+        }
+
+        if (slice != static_cast<int>(slice_))
+        {
+          SetSlice(slice);
+        }
+      }
+    }
+
+    void SetSlice(size_t slice)
+    {
+      if (slices_.get() != NULL)
+      {
+        slice_ = slice;
+
+        std::auto_ptr<Slice> tmp(slices_->GetSlice(slice_));
+        widget_.SetSlice(tmp->GetGeometry());
+      }
+    }
+  };
+
+
+
+  class ReferenceLineSource : public IVolumeSlicer
+  {
+  private:
+    class RendererFactory : public LayerReadyMessage::IRendererFactory
+    {
+    private:
+      double                     x1_;
+      double                     y1_;
+      double                     x2_;
+      double                     y2_;
+      const OrthancStone::CoordinateSystem3D&  slice_;
+
+    public:
+      RendererFactory(double x1,
+                      double y1,
+                      double x2,
+                      double y2,
+                      const OrthancStone::CoordinateSystem3D& slice) :
+        x1_(x1),
+        y1_(y1),
+        x2_(x2),
+        y2_(y2),
+        slice_(slice)
+      {
+      }
+
+      virtual ILayerRenderer* CreateRenderer() const
+      {
+        return new LineLayerRenderer(x1_, y1_, x2_, y2_, slice_);
+      }
+    };
+
+    SliceViewerWidget&  otherPlane_;
+
+  public:
+    ReferenceLineSource(OrthancStone::MessageBroker& broker,
+                        SliceViewerWidget&  otherPlane) :
+      IVolumeSlicer(broker),
+      otherPlane_(otherPlane)
+    {
+      BroadcastMessage(IVolumeSlicer::GeometryReadyMessage(*this));
+    }
+
+    virtual bool GetExtent(std::vector<OrthancStone::Vector>& points,
+                           const OrthancStone::CoordinateSystem3D& viewportSlice)
+    {
+      return false;
+    }
+
+    virtual void ScheduleLayerCreation(const OrthancStone::CoordinateSystem3D& viewportSlice)
+    {
+      Slice reference(viewportSlice, 0.001);
+
+      OrthancStone::Vector p, d;
+
+      const OrthancStone::CoordinateSystem3D& slice = otherPlane_.GetSlice();
+
+      // Compute the line of intersection between the two slices
+      if (!OrthancStone::GeometryToolbox::IntersectTwoPlanes(p, d,
+                                                             slice.GetOrigin(), slice.GetNormal(),
+                                                             viewportSlice.GetOrigin(), viewportSlice.GetNormal()))
+      {
+        // The two slice are parallel, don't try and display the intersection
+        BroadcastMessage(IVolumeSlicer::LayerErrorMessage(*this, reference.GetGeometry()));
+      }
+      else
+      {
+        double x1, y1, x2, y2;
+        viewportSlice.ProjectPoint(x1, y1, p);
+        viewportSlice.ProjectPoint(x2, y2, p + 1000.0 * d);
+
+        const OrthancStone::Extent2D extent = otherPlane_.GetSceneExtent();
+
+        if (OrthancStone::GeometryToolbox::ClipLineToRectangle(x1, y1, x2, y2,
+                                                               x1, y1, x2, y2,
+                                                               extent.GetX1(), extent.GetY1(),
+                                                               extent.GetX2(), extent.GetY2()))
+        {
+          RendererFactory factory(x1, y1, x2, y2, slice);
+          BroadcastMessage(IVolumeSlicer::LayerReadyMessage(*this, factory, reference.GetGeometry()));
+        }
+        else
+        {
+          // Error: Parallel slices
+          BroadcastMessage(IVolumeSlicer::LayerErrorMessage(*this, reference.GetGeometry()));
+        }
+      }
+    }
+  };
+}
--- a/Framework/Layers/CircleMeasureTracker.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,106 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "CircleMeasureTracker.h"
-
-#include <stdio.h>
-#include <boost/math/constants/constants.hpp>
-
-namespace OrthancStone
-{
-  CircleMeasureTracker::CircleMeasureTracker(IStatusBar* statusBar,
-                                             const CoordinateSystem3D& slice,
-                                             double x, 
-                                             double y,
-                                             uint8_t red,
-                                             uint8_t green,
-                                             uint8_t blue,
-                                             const Orthanc::Font& font) :
-    statusBar_(statusBar),
-    slice_(slice),
-    x1_(x),
-    y1_(y),
-    x2_(x),
-    y2_(y),
-    font_(font)
-  {
-    color_[0] = red;
-    color_[1] = green;
-    color_[2] = blue;
-  }
-    
-
-  void CircleMeasureTracker::Render(CairoContext& context,
-                                    double zoom)
-  {
-    double x = (x1_ + x2_) / 2.0;
-    double y = (y1_ + y2_) / 2.0;
-
-    Vector tmp;
-    LinearAlgebra::AssignVector(tmp, x2_ - x1_, y2_ - y1_);
-    double r = boost::numeric::ublas::norm_2(tmp) / 2.0;
-
-    context.SetSourceColor(color_[0], color_[1], color_[2]);
-
-    cairo_t* cr = context.GetObject();
-    cairo_save(cr);
-    cairo_set_line_width(cr, 2.0 / zoom);
-    cairo_translate(cr, x, y);
-    cairo_arc(cr, 0, 0, r, 0, 2.0 * boost::math::constants::pi<double>());
-    cairo_stroke_preserve(cr);
-    cairo_stroke(cr);
-    cairo_restore(cr);
-
-    context.DrawText(font_, FormatRadius(), x, y, BitmapAnchor_Center);
-  }
-    
-
-  double CircleMeasureTracker::GetRadius() const  // In millimeters
-  {
-    Vector a = slice_.MapSliceToWorldCoordinates(x1_, y1_);
-    Vector b = slice_.MapSliceToWorldCoordinates(x2_, y2_);
-    return boost::numeric::ublas::norm_2(b - a) / 2.0;
-  }
-
-
-  std::string CircleMeasureTracker::FormatRadius() const
-  {
-    char buf[64];
-    sprintf(buf, "%0.01f cm", GetRadius() / 10.0);
-    return buf;
-  }
-
-  void CircleMeasureTracker::MouseMove(int displayX,
-                                       int displayY,
-                                       double x,
-                                       double y,
-                                       const std::vector<Touch>& displayTouches,
-                                       const std::vector<Touch>& sceneTouches)
-  {
-    x2_ = x;
-    y2_ = y;
-
-    if (statusBar_ != NULL)
-    {
-      statusBar_->SetMessage("Circle radius: " + FormatRadius());
-    }
-  }
-}
--- a/Framework/Layers/CircleMeasureTracker.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "../Widgets/IWorldSceneMouseTracker.h"
-
-#include "../Viewport/IStatusBar.h"
-#include "../Toolbox/CoordinateSystem3D.h"
-
-#include <Core/Images/Font.h>
-
-namespace OrthancStone
-{
-  class CircleMeasureTracker : public IWorldSceneMouseTracker
-  {
-  private:
-    IStatusBar*           statusBar_;
-    CoordinateSystem3D    slice_;
-    double                x1_;
-    double                y1_;
-    double                x2_;
-    double                y2_;
-    uint8_t               color_[3];
-    const Orthanc::Font&  font_;
-
-  public:
-    CircleMeasureTracker(IStatusBar* statusBar,
-                         const CoordinateSystem3D& slice,
-                         double x, 
-                         double y,
-                         uint8_t red,
-                         uint8_t green,
-                         uint8_t blue,
-                         const Orthanc::Font& font);
-    
-    virtual bool HasRender() const
-    {
-      return true;
-    }
-
-    virtual void Render(CairoContext& context,
-                        double zoom);
-    
-    double GetRadius() const;  // In millimeters
-
-    std::string FormatRadius() const;
-
-    virtual void MouseUp()
-    {
-      // Possibly create a new landmark "volume" with the circle in subclasses
-    }
-
-    virtual void MouseMove(int displayX,
-                           int displayY,
-                           double x,
-                           double y,
-                           const std::vector<Touch>& displayTouches,
-                           const std::vector<Touch>& sceneTouches);
-  };
-}
--- a/Framework/Layers/ColorFrameRenderer.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "ColorFrameRenderer.h"
-
-#include <Core/OrthancException.h>
-#include <Core/Images/Image.h>
-#include <Core/Images/ImageProcessing.h>
-
-namespace OrthancStone
-{
-  CairoSurface* ColorFrameRenderer::GenerateDisplay(const RenderStyle& style)
-  {
-    std::auto_ptr<CairoSurface> display(new CairoSurface(frame_->GetWidth(), frame_->GetHeight(), false /* no alpha */));
-
-    Orthanc::ImageAccessor target;
-    display->GetWriteableAccessor(target);
-    
-    Orthanc::ImageProcessing::Convert(target, *frame_);
-
-    return display.release();
-  }
-
-
-  ColorFrameRenderer::ColorFrameRenderer(const Orthanc::ImageAccessor& frame,
-                                         const CoordinateSystem3D& framePlane,
-                                         double pixelSpacingX,
-                                         double pixelSpacingY,
-                                         bool isFullQuality) :
-    FrameRenderer(framePlane, pixelSpacingX, pixelSpacingY, isFullQuality),
-    frame_(Orthanc::Image::Clone(frame))
-  {
-    if (frame_.get() == NULL)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-
-    if (frame_->GetFormat() != Orthanc::PixelFormat_RGB24)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
-    }
-  }
-}
--- a/Framework/Layers/ColorFrameRenderer.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "FrameRenderer.h"
-
-namespace OrthancStone
-{
-  class ColorFrameRenderer : public FrameRenderer
-  {
-  private:
-    std::auto_ptr<Orthanc::ImageAccessor>   frame_;  // In RGB24
-
-  protected:
-    virtual CairoSurface* GenerateDisplay(const RenderStyle& style);
-
-  public:
-    ColorFrameRenderer(const Orthanc::ImageAccessor& frame,
-                       const CoordinateSystem3D& framePlane,
-                       double pixelSpacingX,
-                       double pixelSpacingY,
-                       bool isFullQuality);
-  };
-}
--- a/Framework/Layers/DicomSeriesVolumeSlicer.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,161 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "DicomSeriesVolumeSlicer.h"
-
-#include "FrameRenderer.h"
-#include "../Toolbox/DicomFrameConverter.h"
-
-#include <Core/Logging.h>
-#include <Core/OrthancException.h>
-
-#include <boost/lexical_cast.hpp>
-
-namespace OrthancStone
-{
-
-  void DicomSeriesVolumeSlicer::OnSliceGeometryReady(const OrthancSlicesLoader::SliceGeometryReadyMessage& message)
-  {
-    if (message.GetOrigin().GetSlicesCount() > 0)
-    {
-      BroadcastMessage(IVolumeSlicer::GeometryReadyMessage(*this));
-    }
-    else
-    {
-      BroadcastMessage(IVolumeSlicer::GeometryErrorMessage(*this));
-    }
-  }
-
-  void DicomSeriesVolumeSlicer::OnSliceGeometryError(const OrthancSlicesLoader::SliceGeometryErrorMessage& message)
-  {
-    BroadcastMessage(IVolumeSlicer::GeometryErrorMessage(*this));
-  }
-
-
-  class DicomSeriesVolumeSlicer::RendererFactory : public LayerReadyMessage::IRendererFactory
-  {
-  private:
-    const OrthancSlicesLoader::SliceImageReadyMessage&  message_;
-
-  public:
-    RendererFactory(const OrthancSlicesLoader::SliceImageReadyMessage& message) :
-      message_(message)
-    {
-    }
-
-    virtual ILayerRenderer* CreateRenderer() const
-    {
-      bool isFull = (message_.GetEffectiveQuality() == SliceImageQuality_FullPng ||
-                     message_.GetEffectiveQuality() == SliceImageQuality_FullPam);
-
-      return FrameRenderer::CreateRenderer(message_.GetImage(), message_.GetSlice(), isFull);
-    }
-  };
-
-  void DicomSeriesVolumeSlicer::OnSliceImageReady(const OrthancSlicesLoader::SliceImageReadyMessage& message)
-  {
-    // first notify that the pixel data of the frame is ready (targeted to, i.e: an image cache)
-    BroadcastMessage(FrameReadyMessage(*this, message.GetImage(), 
-                                  message.GetEffectiveQuality(), message.GetSlice()));
-
-    // then notify that the layer is ready for rendering
-    RendererFactory factory(message);
-    BroadcastMessage(IVolumeSlicer::LayerReadyMessage(*this, factory, message.GetSlice().GetGeometry()));
-  }
-
-  void DicomSeriesVolumeSlicer::OnSliceImageError(const OrthancSlicesLoader::SliceImageErrorMessage& message)
-  {
-    BroadcastMessage(IVolumeSlicer::LayerErrorMessage(*this, message.GetSlice().GetGeometry()));
-  }
-
-
-  DicomSeriesVolumeSlicer::DicomSeriesVolumeSlicer(MessageBroker& broker, OrthancApiClient& orthanc) :
-    IVolumeSlicer(broker),
-    IObserver(broker),
-    loader_(broker, orthanc),
-    quality_(SliceImageQuality_FullPng)
-  {
-    loader_.RegisterObserverCallback(
-      new Callable<DicomSeriesVolumeSlicer, OrthancSlicesLoader::SliceGeometryReadyMessage>
-        (*this, &DicomSeriesVolumeSlicer::OnSliceGeometryReady));
-
-    loader_.RegisterObserverCallback(
-      new Callable<DicomSeriesVolumeSlicer, OrthancSlicesLoader::SliceGeometryErrorMessage>
-      (*this, &DicomSeriesVolumeSlicer::OnSliceGeometryError));
-
-    loader_.RegisterObserverCallback(
-      new Callable<DicomSeriesVolumeSlicer, OrthancSlicesLoader::SliceImageReadyMessage>
-        (*this, &DicomSeriesVolumeSlicer::OnSliceImageReady));
-
-    loader_.RegisterObserverCallback(
-      new Callable<DicomSeriesVolumeSlicer, OrthancSlicesLoader::SliceImageErrorMessage>
-      (*this, &DicomSeriesVolumeSlicer::OnSliceImageError));
-  }
-
-  
-  void DicomSeriesVolumeSlicer::LoadSeries(const std::string& seriesId)
-  {
-    loader_.ScheduleLoadSeries(seriesId);
-  }
-
-
-  void DicomSeriesVolumeSlicer::LoadInstance(const std::string& instanceId)
-  {
-    loader_.ScheduleLoadInstance(instanceId);
-  }
-
-
-  void DicomSeriesVolumeSlicer::LoadFrame(const std::string& instanceId,
-                                          unsigned int frame)
-  {
-    loader_.ScheduleLoadFrame(instanceId, frame);
-  }
-
-
-  bool DicomSeriesVolumeSlicer::GetExtent(std::vector<Vector>& points,
-                                          const CoordinateSystem3D& viewportSlice)
-  {
-    size_t index;
-
-    if (loader_.IsGeometryReady() &&
-        loader_.LookupSlice(index, viewportSlice))
-    {
-      loader_.GetSlice(index).GetExtent(points);
-      return true;
-    }
-    else
-    {
-      return false;
-    }
-  }
-
-  
-  void DicomSeriesVolumeSlicer::ScheduleLayerCreation(const CoordinateSystem3D& viewportSlice)
-  {
-    size_t index;
-
-    if (loader_.IsGeometryReady() &&
-        loader_.LookupSlice(index, viewportSlice))
-    {
-      loader_.ScheduleLoadSliceImage(index, quality_);
-    }
-  }
-}
--- a/Framework/Layers/DicomSeriesVolumeSlicer.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,126 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "IVolumeSlicer.h"
-#include "../Toolbox/IWebService.h"
-#include "../Toolbox/OrthancSlicesLoader.h"
-#include "../Toolbox/OrthancApiClient.h"
-
-namespace OrthancStone
-{  
-  // this class is in charge of loading a Frame.
-  // once it's been loaded (first the geometry and then the image),
-  // messages are sent to observers so they can use it
-  class DicomSeriesVolumeSlicer :
-    public IVolumeSlicer,
-    public IObserver
-    //private OrthancSlicesLoader::ISliceLoaderObserver
-  {
-  public:
-    // TODO: Add "frame" and "instanceId"
-    class FrameReadyMessage : public OriginMessage<DicomSeriesVolumeSlicer>
-    {
-      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
-      
-    private:
-      const Orthanc::ImageAccessor&  frame_;
-      SliceImageQuality              imageQuality_;
-      const Deprecated::Slice&                   slice_;
-
-    public:
-      FrameReadyMessage(DicomSeriesVolumeSlicer& origin,
-                        const Orthanc::ImageAccessor& frame,
-                        SliceImageQuality imageQuality,
-                        const Deprecated::Slice& slice) :
-        OriginMessage(origin),
-        frame_(frame),
-        imageQuality_(imageQuality),
-        slice_(slice)
-      {
-      }
-
-      const Orthanc::ImageAccessor& GetFrame() const
-      {
-        return frame_;
-      }
-
-      SliceImageQuality GetImageQuality() const
-      {
-        return imageQuality_;
-      }
-
-      const Deprecated::Slice& GetSlice() const
-      {
-        return slice_;
-      }
-    };
-
-    
-  private:
-    class RendererFactory;
-    
-    OrthancSlicesLoader  loader_;
-    SliceImageQuality    quality_;
-
-  public:
-    DicomSeriesVolumeSlicer(MessageBroker& broker, OrthancApiClient& orthanc);
-
-    void LoadSeries(const std::string& seriesId);
-
-    void LoadInstance(const std::string& instanceId);
-
-    void LoadFrame(const std::string& instanceId,
-                   unsigned int frame);
-
-    void SetImageQuality(SliceImageQuality quality)
-    {
-      quality_ = quality;
-    }
-
-    SliceImageQuality GetImageQuality() const
-    {
-      return quality_;
-    }
-
-    size_t GetSlicesCount() const
-    {
-      return loader_.GetSlicesCount();
-    }
-
-    const Deprecated::Slice& GetSlice(size_t slice) const 
-    {
-      return loader_.GetSlice(slice);
-    }
-
-    virtual bool GetExtent(std::vector<Vector>& points,
-                           const CoordinateSystem3D& viewportSlice);
-
-    virtual void ScheduleLayerCreation(const CoordinateSystem3D& viewportSlice);
-
-protected:
-    void OnSliceGeometryReady(const OrthancSlicesLoader::SliceGeometryReadyMessage& message);
-    void OnSliceGeometryError(const OrthancSlicesLoader::SliceGeometryErrorMessage& message);
-    void OnSliceImageReady(const OrthancSlicesLoader::SliceImageReadyMessage& message);
-    void OnSliceImageError(const OrthancSlicesLoader::SliceImageErrorMessage& message);
-  };
-}
--- a/Framework/Layers/DicomStructureSetSlicer.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,170 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "DicomStructureSetSlicer.h"
-
-namespace OrthancStone
-{
-  class DicomStructureSetSlicer::Renderer : public ILayerRenderer
-  {
-  private:
-    class Structure
-    {
-    private:
-      bool                                                         visible_;
-      uint8_t                                                      red_;
-      uint8_t                                                      green_;
-      uint8_t                                                      blue_;
-      std::string                                                  name_;
-      std::vector< std::vector<DicomStructureSet::PolygonPoint> >  polygons_;
-
-    public:
-      Structure(DicomStructureSet& structureSet,
-                const CoordinateSystem3D& plane,
-                size_t index) :
-        name_(structureSet.GetStructureName(index))
-      {
-        structureSet.GetStructureColor(red_, green_, blue_, index);
-        visible_ = structureSet.ProjectStructure(polygons_, index, plane);
-      }
-
-      void Render(CairoContext& context)
-      {
-        if (visible_)
-        {
-          cairo_t* cr = context.GetObject();
-        
-          context.SetSourceColor(red_, green_, blue_);
-
-          for (size_t i = 0; i < polygons_.size(); i++)
-          {
-            cairo_move_to(cr, polygons_[i][0].first, polygons_[i][0].second);
-
-            for (size_t j = 1; j < polygons_[i].size(); j++)
-            {
-              cairo_line_to(cr, polygons_[i][j].first, polygons_[i][j].second);
-            }
-
-            cairo_line_to(cr, polygons_[i][0].first, polygons_[i][0].second);
-            cairo_stroke(cr);
-          }
-        }
-      }
-    };
-
-    typedef std::list<Structure*>  Structures;
-    
-    CoordinateSystem3D  plane_;
-    Structures          structures_;
-    
-  public:
-    Renderer(DicomStructureSet& structureSet,
-             const CoordinateSystem3D& plane) :
-      plane_(plane)
-    {
-      for (size_t k = 0; k < structureSet.GetStructureCount(); k++)
-      {
-        structures_.push_back(new Structure(structureSet, plane, k));
-      }
-    }
-
-    virtual ~Renderer()
-    {
-      for (Structures::iterator it = structures_.begin();
-           it != structures_.end(); ++it)
-      {
-        delete *it;
-      }
-    }
-
-    virtual bool RenderLayer(CairoContext& context,
-                             const ViewportGeometry& view)
-    {
-      cairo_set_line_width(context.GetObject(), 2.0f / view.GetZoom());
-
-      for (Structures::const_iterator it = structures_.begin();
-           it != structures_.end(); ++it)
-      {
-        assert(*it != NULL);
-        (*it)->Render(context);
-      }
-
-      return true;
-    }
-
-    virtual const CoordinateSystem3D& GetLayerPlane()
-    {
-      return plane_;
-    }
-
-    virtual void SetLayerStyle(const RenderStyle& style)
-    {
-    }
-    
-    virtual bool IsFullQuality()
-    {
-      return true;
-    }
-  };
-
-
-  class DicomStructureSetSlicer::RendererFactory : public LayerReadyMessage::IRendererFactory
-  {
-  private:
-    DicomStructureSet&         structureSet_;
-    const CoordinateSystem3D&  plane_;
-
-  public:
-    RendererFactory(DicomStructureSet& structureSet,
-                    const CoordinateSystem3D&  plane) :
-      structureSet_(structureSet),
-      plane_(plane)
-    {
-    }
-
-    virtual ILayerRenderer* CreateRenderer() const
-    {
-      return new Renderer(structureSet_, plane_);
-    }
-  };
-  
-
-  DicomStructureSetSlicer::DicomStructureSetSlicer(MessageBroker& broker,
-                                                   StructureSetLoader& loader) :
-    IVolumeSlicer(broker),
-    IObserver(broker),
-    loader_(loader)
-  {
-    loader_.RegisterObserverCallback(
-      new Callable<DicomStructureSetSlicer, StructureSetLoader::ContentChangedMessage>
-      (*this, &DicomStructureSetSlicer::OnStructureSetLoaded));
-  }
-
-
-  void DicomStructureSetSlicer::ScheduleLayerCreation(const CoordinateSystem3D& viewportPlane)
-  {
-    if (loader_.HasStructureSet())
-    {
-      RendererFactory factory(loader_.GetStructureSet(), viewportPlane);
-      BroadcastMessage(IVolumeSlicer::LayerReadyMessage(*this, factory, viewportPlane));
-    }
-  }
-}
--- a/Framework/Layers/DicomStructureSetSlicer.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "IVolumeSlicer.h"
-#include "../Volumes/StructureSetLoader.h"
-
-namespace OrthancStone
-{
-  class DicomStructureSetSlicer :
-    public IVolumeSlicer,
-    public IObserver
-  {
-  private:
-    class Renderer;
-    class RendererFactory;
-
-    StructureSetLoader& loader_;
-
-    void OnStructureSetLoaded(const IVolumeLoader::ContentChangedMessage& message)
-    {
-      BroadcastMessage(IVolumeSlicer::ContentChangedMessage(*this));
-    }
-
-  public:
-    DicomStructureSetSlicer(MessageBroker& broker,
-                            StructureSetLoader& loader);
-
-    virtual bool GetExtent(std::vector<Vector>& points,
-                           const CoordinateSystem3D& viewportPlane)
-    {
-      return false;
-    }
-
-    virtual void ScheduleLayerCreation(const CoordinateSystem3D& viewportPlane);
-  };
-}
--- a/Framework/Layers/FrameRenderer.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,140 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "FrameRenderer.h"
-
-#include "GrayscaleFrameRenderer.h"
-#include "ColorFrameRenderer.h"
-
-#include <Core/OrthancException.h>
-
-namespace OrthancStone
-{
-  FrameRenderer::FrameRenderer(const CoordinateSystem3D& framePlane,
-                               double pixelSpacingX,
-                               double pixelSpacingY,
-                               bool isFullQuality) :
-    framePlane_(framePlane),
-    pixelSpacingX_(pixelSpacingX),
-    pixelSpacingY_(pixelSpacingY),
-    isFullQuality_(isFullQuality)
-  {
-  }
-
-
-  bool FrameRenderer::RenderLayer(CairoContext& context,
-                                  const ViewportGeometry& view)
-  {    
-    if (!style_.visible_)
-    {
-      return true;
-    }
-
-    if (display_.get() == NULL)
-    {
-      display_.reset(GenerateDisplay(style_));
-    }
-
-    assert(display_.get() != NULL);
-
-    cairo_t *cr = context.GetObject();
-
-    cairo_save(cr);
-
-    cairo_matrix_t transform;
-    cairo_matrix_init_identity(&transform);
-    cairo_matrix_scale(&transform, pixelSpacingX_, pixelSpacingY_);
-    cairo_matrix_translate(&transform, -0.5, -0.5);
-    cairo_transform(cr, &transform);
-
-    //cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
-    cairo_set_source_surface(cr, display_->GetObject(), 0, 0);
-
-    switch (style_.interpolation_)
-    {
-      case ImageInterpolation_Nearest:
-        cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST);
-        break;
-
-      case ImageInterpolation_Bilinear:
-        cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_BILINEAR);
-        break;
-
-      default:
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-
-    cairo_paint_with_alpha(cr, style_.alpha_);
-
-    if (style_.drawGrid_)
-    {
-      context.SetSourceColor(style_.drawColor_);
-      cairo_set_line_width(cr, 0.5 / view.GetZoom());
-
-      for (unsigned int x = 0; x <= display_->GetWidth(); x++)
-      {
-        cairo_move_to(cr, x, 0);
-        cairo_line_to(cr, x, display_->GetHeight());
-      }
-
-      for (unsigned int y = 0; y <= display_->GetHeight(); y++)
-      {
-        cairo_move_to(cr, 0, y);
-        cairo_line_to(cr, display_->GetWidth(), y);
-      }
-
-      cairo_stroke(cr);
-    }
-
-    cairo_restore(cr);
-
-    return true;
-  }
-
-
-  void FrameRenderer::SetLayerStyle(const RenderStyle& style)
-  {
-    style_ = style;
-    display_.reset(NULL);
-  }
-
-
-  ILayerRenderer* FrameRenderer::CreateRenderer(const Orthanc::ImageAccessor& frame,
-                                                const Deprecated::Slice& framePlane,
-                                                bool isFullQuality)
-  {
-    if (frame.GetFormat() == Orthanc::PixelFormat_RGB24)
-    {
-      return new ColorFrameRenderer(frame,
-                                    framePlane.GetGeometry(), 
-                                    framePlane.GetPixelSpacingX(),
-                                    framePlane.GetPixelSpacingY(), isFullQuality);
-    }
-    else
-    {
-      return new GrayscaleFrameRenderer(frame,
-                                        framePlane.GetConverter(),
-                                        framePlane.GetGeometry(), 
-                                        framePlane.GetPixelSpacingX(),
-                                        framePlane.GetPixelSpacingY(), isFullQuality);
-    }
-  }
-}
--- a/Framework/Layers/FrameRenderer.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "ILayerRenderer.h"
-
-#include "../Toolbox/Slice.h"
-
-namespace OrthancStone
-{
-  class FrameRenderer : public ILayerRenderer
-  {
-  private:
-    CoordinateSystem3D            framePlane_;
-    double                        pixelSpacingX_;
-    double                        pixelSpacingY_;
-    RenderStyle                   style_;
-    bool                          isFullQuality_;
-    std::auto_ptr<CairoSurface>   display_;
-
-  protected:
-    virtual CairoSurface* GenerateDisplay(const RenderStyle& style) = 0;
-
-  public:
-    FrameRenderer(const CoordinateSystem3D& framePlane,
-                  double pixelSpacingX,
-                  double pixelSpacingY,
-                  bool isFullQuality);
-
-    virtual bool RenderLayer(CairoContext& context,
-                             const ViewportGeometry& view);
-
-    virtual const CoordinateSystem3D& GetLayerPlane()
-    {
-      return framePlane_;
-    }
-
-    virtual void SetLayerStyle(const RenderStyle& style);
-
-    virtual bool IsFullQuality() 
-    {
-      return isFullQuality_;
-    }
-
-    // TODO: Avoid cloning the "frame"
-    static ILayerRenderer* CreateRenderer(const Orthanc::ImageAccessor& frame,
-                                          const Deprecated::Slice& framePlane,
-                                          bool isFullQuality);
-  };
-}
--- a/Framework/Layers/GrayscaleFrameRenderer.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,141 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "GrayscaleFrameRenderer.h"
-
-#include <Core/Images/Image.h>
-#include <Core/OrthancException.h>
-
-namespace OrthancStone
-{
-  CairoSurface* GrayscaleFrameRenderer::GenerateDisplay(const RenderStyle& style)
-  {
-    assert(frame_->GetFormat() == Orthanc::PixelFormat_Float32);
-
-    std::auto_ptr<CairoSurface> result;
-
-    float windowCenter, windowWidth;
-    style.ComputeWindowing(windowCenter, windowWidth,
-                           defaultWindowCenter_, defaultWindowWidth_);
-
-    float x0 = windowCenter - windowWidth / 2.0f;
-    float x1 = windowCenter + windowWidth / 2.0f;
-
-    //LOG(INFO) << "Window: " << x0 << " => " << x1;
-
-    result.reset(new CairoSurface(frame_->GetWidth(), frame_->GetHeight(), false /* no alpha */));
-
-    const uint8_t* lut = NULL;
-    if (style.applyLut_)
-    {
-      if (Orthanc::EmbeddedResources::GetFileResourceSize(style.lut_) != 3 * 256)
-      {
-        // Invalid colormap
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-      }
-
-      lut = reinterpret_cast<const uint8_t*>(Orthanc::EmbeddedResources::GetFileResourceBuffer(style.lut_));
-    }
-
-    Orthanc::ImageAccessor target;
-    result->GetWriteableAccessor(target);
-    
-    const unsigned int width = target.GetWidth();
-    const unsigned int height = target.GetHeight();
-    
-    for (unsigned int y = 0; y < height; y++)
-    {
-      const float* p = reinterpret_cast<const float*>(frame_->GetConstRow(y));
-      uint8_t* q = reinterpret_cast<uint8_t*>(target.GetRow(y));
-
-      for (unsigned int x = 0; x < width; x++, p++, q += 4)
-      {
-        uint8_t v = 0;
-        if (windowWidth >= 0.001f)  // Avoid division by zero
-        {
-          if (*p >= x1)
-          {
-            v = 255;
-          }
-          else if (*p <= x0)
-          {
-            v = 0;
-          }
-          else
-          {
-            // https://en.wikipedia.org/wiki/Linear_interpolation
-            v = static_cast<uint8_t>(255.0f * (*p - x0) / (x1 - x0));
-          }
-
-          if (style.reverse_ ^ (photometric_ == Orthanc::PhotometricInterpretation_Monochrome1))
-          {
-            v = 255 - v;
-          }
-        }
-
-        if (style.applyLut_)
-        {
-          assert(lut != NULL);
-          q[3] = 255;
-          q[2] = lut[3 * v];
-          q[1] = lut[3 * v + 1];
-          q[0] = lut[3 * v + 2];
-        }
-        else
-        {
-          q[3] = 255;
-          q[2] = v;
-          q[1] = v;
-          q[0] = v;
-        }
-      }
-    }
-
-    return result.release();
-  }
-
-
-  GrayscaleFrameRenderer::GrayscaleFrameRenderer(const Orthanc::ImageAccessor& frame,
-                                                 const Deprecated::DicomFrameConverter& converter,
-                                                 const CoordinateSystem3D& framePlane,
-                                                 double pixelSpacingX,
-                                                 double pixelSpacingY,
-                                                 bool isFullQuality) :
-    FrameRenderer(framePlane, pixelSpacingX, pixelSpacingY, isFullQuality),
-    frame_(Orthanc::Image::Clone(frame)),
-    defaultWindowCenter_(static_cast<float>(converter.GetDefaultWindowCenter())),
-    defaultWindowWidth_(static_cast<float>(converter.GetDefaultWindowWidth())),
-    photometric_(converter.GetPhotometricInterpretation())
-  {
-    if (frame_.get() == NULL)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-
-    converter.ConvertFrameInplace(frame_);
-    assert(frame_.get() != NULL);
-
-    if (frame_->GetFormat() != Orthanc::PixelFormat_Float32)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
-    }
-  }
-}
--- a/Framework/Layers/GrayscaleFrameRenderer.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "FrameRenderer.h"
-#include "../Toolbox/DicomFrameConverter.h"
-
-namespace OrthancStone
-{
-  class GrayscaleFrameRenderer : public FrameRenderer
-  {
-  private:
-    std::auto_ptr<Orthanc::ImageAccessor>   frame_;  // In Float32
-    float                                   defaultWindowCenter_;
-    float                                   defaultWindowWidth_;
-    Orthanc::PhotometricInterpretation      photometric_;
-
-  protected:
-    virtual CairoSurface* GenerateDisplay(const RenderStyle& style);
-
-  public:
-    GrayscaleFrameRenderer(const Orthanc::ImageAccessor& frame,
-                           const Deprecated::DicomFrameConverter& converter,
-                           const CoordinateSystem3D& framePlane,
-                           double pixelSpacingX,
-                           double pixelSpacingY,
-                           bool isFullQuality);
-  };
-}
--- a/Framework/Layers/ILayerRenderer.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "../Viewport/CairoContext.h"
-#include "../Toolbox/CoordinateSystem3D.h"
-#include "../Toolbox/ViewportGeometry.h"
-#include "RenderStyle.h"
-
-namespace OrthancStone
-{
-  class ILayerRenderer : public boost::noncopyable
-  {
-  public:
-    virtual ~ILayerRenderer()
-    {
-    }
-    
-    virtual bool RenderLayer(CairoContext& context,
-                             const ViewportGeometry& view) = 0;
-
-    virtual void SetLayerStyle(const RenderStyle& style) = 0;
-
-    virtual const CoordinateSystem3D& GetLayerPlane() = 0;
-    
-    virtual bool IsFullQuality() = 0;
-  };
-}
--- a/Framework/Layers/IVolumeSlicer.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,139 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "ILayerRenderer.h"
-#include "../Toolbox/Slice.h"
-#include "../../Framework/Messages/IObservable.h"
-#include "../../Framework/Messages/IMessage.h"
-#include "Core/Images/Image.h"
-#include <boost/shared_ptr.hpp>
-
-namespace OrthancStone
-{
-  class IVolumeSlicer : public IObservable
-  {
-  public:
-    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryReadyMessage, IVolumeSlicer);
-    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryErrorMessage, IVolumeSlicer);
-    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, ContentChangedMessage, IVolumeSlicer);
-
-    class SliceContentChangedMessage : public OriginMessage<IVolumeSlicer>
-    {
-      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
-      
-    private:
-      const Deprecated::Slice& slice_;
-
-    public:
-      SliceContentChangedMessage(IVolumeSlicer& origin,
-                                 const Deprecated::Slice& slice) :
-        OriginMessage(origin),
-        slice_(slice)
-      {
-      }
-
-      const Deprecated::Slice& GetSlice() const
-      {
-        return slice_;
-      }
-    };
-    
-
-    class LayerReadyMessage : public OriginMessage<IVolumeSlicer>
-    {
-      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
-      
-    public:
-      class IRendererFactory : public boost::noncopyable
-      {
-      public:
-        virtual ~IRendererFactory()
-        {
-        }
-
-        virtual ILayerRenderer* CreateRenderer() const = 0;
-      };
-    
-    private:
-      const IRendererFactory&    factory_;
-      const CoordinateSystem3D&  slice_;
-
-    public:
-      LayerReadyMessage(IVolumeSlicer& origin,
-                        const IRendererFactory& rendererFactory,
-                        const CoordinateSystem3D& slice) :
-        OriginMessage(origin),
-        factory_(rendererFactory),
-        slice_(slice)
-      {
-      }
-
-      ILayerRenderer* CreateRenderer() const
-      {
-        return factory_.CreateRenderer();
-      }
-
-      const CoordinateSystem3D& GetSlice() const
-      {
-        return slice_;
-      }
-    };
-
-
-    class LayerErrorMessage : public OriginMessage<IVolumeSlicer>
-    {
-      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
-      
-    private:
-      const CoordinateSystem3D&  slice_;
-
-    public:
-      LayerErrorMessage(IVolumeSlicer& origin,
-                        const CoordinateSystem3D& slice) :
-        OriginMessage(origin),
-        slice_(slice)
-      {
-      }
-
-      const CoordinateSystem3D& GetSlice() const
-      {
-        return slice_;
-      }
-    };
-
-
-    IVolumeSlicer(MessageBroker& broker) :
-      IObservable(broker)
-    {
-    }
-    
-    virtual ~IVolumeSlicer()
-    {
-    }
-
-    virtual bool GetExtent(std::vector<Vector>& points,
-                           const CoordinateSystem3D& viewportSlice) = 0;
-
-    virtual void ScheduleLayerCreation(const CoordinateSystem3D& viewportSlice) = 0;
-  };
-}
--- a/Framework/Layers/LineLayerRenderer.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "LineLayerRenderer.h"
-
-namespace OrthancStone
-{
-  LineLayerRenderer::LineLayerRenderer(double x1,
-                                       double y1,
-                                       double x2,
-                                       double y2,
-                                       const CoordinateSystem3D& plane) : 
-    x1_(x1),
-    y1_(y1),
-    x2_(x2),
-    y2_(y2),
-    plane_(plane)
-  {
-    RenderStyle style;
-    SetLayerStyle(style);
-  }
-
-
-  bool LineLayerRenderer::RenderLayer(CairoContext& context,
-                                      const ViewportGeometry& view)
-  {
-    if (visible_)
-    {
-      context.SetSourceColor(color_);
-
-      cairo_t *cr = context.GetObject();
-      cairo_set_line_width(cr, 1.0 / view.GetZoom());
-      cairo_move_to(cr, x1_, y1_);
-      cairo_line_to(cr, x2_, y2_);
-      cairo_stroke(cr);
-    }
-
-    return true;
-  }
-
-
-  void LineLayerRenderer::SetLayerStyle(const RenderStyle& style)
-  {
-    visible_ = style.visible_;
-    color_[0] = style.drawColor_[0];
-    color_[1] = style.drawColor_[1];
-    color_[2] = style.drawColor_[2];
-  }
-}
--- a/Framework/Layers/LineLayerRenderer.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "ILayerRenderer.h"
-
-namespace OrthancStone
-{
-  class LineLayerRenderer : public ILayerRenderer
-  {
-  private:
-    double              x1_;
-    double              y1_;
-    double              x2_;
-    double              y2_;
-    CoordinateSystem3D  plane_;
-    bool                visible_;
-    uint8_t             color_[3];
-
-  public:
-    LineLayerRenderer(double x1,
-                      double y1,
-                      double x2,
-                      double y2,
-                      const CoordinateSystem3D& plane);
-
-    virtual bool RenderLayer(CairoContext& context,
-                             const ViewportGeometry& view);
-
-    virtual void SetLayerStyle(const RenderStyle& style);
-
-    virtual const CoordinateSystem3D& GetLayerPlane()
-    {
-      return plane_;
-    }
-    
-    virtual bool IsFullQuality()
-    {
-      return true;
-    }
-  };
-}
--- a/Framework/Layers/LineMeasureTracker.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,102 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "LineMeasureTracker.h"
-
-#include <stdio.h>
-
-namespace OrthancStone
-{
-  LineMeasureTracker::LineMeasureTracker(IStatusBar* statusBar,
-                                         const CoordinateSystem3D& slice,
-                                         double x, 
-                                         double y,
-                                         uint8_t red,
-                                         uint8_t green,
-                                         uint8_t blue,
-                                         const Orthanc::Font& font) :
-    statusBar_(statusBar),
-    slice_(slice),
-    x1_(x),
-    y1_(y),
-    x2_(x),
-    y2_(y),
-    font_(font)
-  {
-    color_[0] = red;
-    color_[1] = green;
-    color_[2] = blue;
-  }
-    
-
-  void LineMeasureTracker::Render(CairoContext& context,
-                                  double zoom)
-  {
-    context.SetSourceColor(color_[0], color_[1], color_[2]);
-
-    cairo_t* cr = context.GetObject();
-    cairo_set_line_width(cr, 2.0 / zoom);
-    cairo_move_to(cr, x1_, y1_);
-    cairo_line_to(cr, x2_, y2_);
-    cairo_stroke(cr);
-
-    if (y2_ - y1_ < 0)
-    {
-      context.DrawText(font_, FormatLength(), x2_, y2_ - 5, BitmapAnchor_BottomCenter);
-    }
-    else
-    {
-      context.DrawText(font_, FormatLength(), x2_, y2_ + 5, BitmapAnchor_TopCenter);
-    }
-  }
-    
-
-  double LineMeasureTracker::GetLength() const  // In millimeters
-  {
-    Vector a = slice_.MapSliceToWorldCoordinates(x1_, y1_);
-    Vector b = slice_.MapSliceToWorldCoordinates(x2_, y2_);
-    return boost::numeric::ublas::norm_2(b - a);
-  }
-
-
-  std::string LineMeasureTracker::FormatLength() const
-  {
-    char buf[64];
-    sprintf(buf, "%0.01f cm", GetLength() / 10.0);
-    return buf;
-  }
-
-  void LineMeasureTracker::MouseMove(int displayX,
-                                     int displayY,
-                                     double x,
-                                     double y,
-                                     const std::vector<Touch>& displayTouches,
-                                     const std::vector<Touch>& sceneTouches)
-  {
-    x2_ = x;
-    y2_ = y;
-
-    if (statusBar_ != NULL)
-    {
-      statusBar_->SetMessage("Line length: " + FormatLength());
-    }
-  }
-}
--- a/Framework/Layers/LineMeasureTracker.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "../Widgets/IWorldSceneMouseTracker.h"
-
-#include "../Viewport/IStatusBar.h"
-#include "../Toolbox/CoordinateSystem3D.h"
-
-namespace OrthancStone
-{
-  class LineMeasureTracker : public IWorldSceneMouseTracker
-  {
-  private:
-    IStatusBar*           statusBar_;
-    CoordinateSystem3D    slice_;
-    double                x1_;
-    double                y1_;
-    double                x2_;
-    double                y2_;
-    uint8_t               color_[3];
-    unsigned int          fontSize_;
-    const Orthanc::Font&  font_;
-
-  public:
-    LineMeasureTracker(IStatusBar* statusBar,
-                       const CoordinateSystem3D& slice,
-                       double x, 
-                       double y,
-                       uint8_t red,
-                       uint8_t green,
-                       uint8_t blue,
-                       const Orthanc::Font& font);
-
-    virtual bool HasRender() const
-    {
-      return true;
-    }
-
-    virtual void Render(CairoContext& context,
-                        double zoom);
-    
-    double GetLength() const;  // In millimeters
-
-    std::string FormatLength() const;
-
-    virtual void MouseUp()
-    {
-      // Possibly create a new landmark "volume" with the line in subclasses
-    }
-
-    virtual void MouseMove(int displayX,
-                           int displayY,
-                           double x,
-                           double y,
-                           const std::vector<Touch>& displayTouches,
-                           const std::vector<Touch>& sceneTouches);
-  };
-}
--- a/Framework/Layers/RenderStyle.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "RenderStyle.h"
-
-#include <Core/OrthancException.h>
-
-namespace OrthancStone
-{
-  RenderStyle::RenderStyle()
-  {
-    visible_ = true;
-    reverse_ = false;
-    windowing_ = ImageWindowing_Custom;
-    alpha_ = 1;
-    applyLut_ = false;
-    lut_ = Orthanc::EmbeddedResources::COLORMAP_HOT;
-    drawGrid_ = false;
-    drawColor_[0] = 255;
-    drawColor_[1] = 255;
-    drawColor_[2] = 255;
-    customWindowCenter_ = 128;
-    customWindowWidth_ = 256;
-    interpolation_ = ImageInterpolation_Nearest;
-    fontSize_ = 14;
-  }
-
-
-  void RenderStyle::ComputeWindowing(float& targetCenter,
-                                     float& targetWidth,
-                                     float defaultCenter,
-                                     float defaultWidth) const
-  {
-    if (windowing_ == ImageWindowing_Custom)
-    {
-      targetCenter = customWindowCenter_;
-      targetWidth = customWindowWidth_;
-    }
-    else
-    {
-      return ::OrthancStone::ComputeWindowing
-        (targetCenter, targetWidth, windowing_, defaultCenter, defaultWidth);
-    }
-  }
-
-  
-  void RenderStyle::SetColor(uint8_t red,
-                             uint8_t green,
-                             uint8_t blue)
-  {
-    drawColor_[0] = red;
-    drawColor_[1] = green;
-    drawColor_[2] = blue;
-  }
-}
--- a/Framework/Layers/RenderStyle.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "../StoneEnumerations.h"
-
-#include <EmbeddedResources.h>
-
-#include <stdint.h>
-
-namespace OrthancStone
-{
-  struct RenderStyle
-  {
-    bool visible_;
-    bool reverse_;
-    ImageWindowing windowing_;
-    float alpha_;   // In [0,1]
-    bool applyLut_;
-    Orthanc::EmbeddedResources::FileResourceId  lut_;
-    bool drawGrid_;
-    uint8_t drawColor_[3];
-    float customWindowCenter_;
-    float customWindowWidth_;
-    ImageInterpolation interpolation_;
-    unsigned int fontSize_;
-    
-    RenderStyle();
-
-    void ComputeWindowing(float& targetCenter,
-                          float& targetWidth,
-                          float defaultCenter,
-                          float defaultWidth) const;
-
-    void SetColor(uint8_t red,
-                  uint8_t green,
-                  uint8_t blue);
-  };
-}
--- a/Framework/Layers/SeriesFrameRendererFactory.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,177 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "SeriesFrameRendererFactory.h"
-
-#include "FrameRenderer.h"
-
-#include <OrthancException.h>
-#include <Logging.h>
-#include <Toolbox.h>
-#include <Plugins/Samples/Common/OrthancPluginException.h>
-#include <Plugins/Samples/Common/DicomDatasetReader.h>
-
-
-namespace OrthancStone
-{
-  void SeriesFrameRendererFactory::ReadCurrentFrameDataset(size_t frame)
-  {
-    if (currentDataset_.get() != NULL &&
-        (fast_ || currentFrame_ == frame))
-    {
-      // The frame has not changed since the previous call, no need to
-      // update the DICOM dataset
-      return; 
-    }
-      
-    currentDataset_.reset(loader_->DownloadDicom(frame));
-    currentFrame_ = frame;
-
-    if (currentDataset_.get() == NULL)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-    }
-  }
-
-
-  void SeriesFrameRendererFactory::GetCurrentPixelSpacing(double& spacingX,
-                                                          double& spacingY) const
-  {
-    if (currentDataset_.get() == NULL)
-    {
-      // There was no previous call "ReadCurrentFrameDataset()"
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-    }
-    
-    GeometryToolbox::GetPixelSpacing(spacingX, spacingY, *currentDataset_);
-  }
-
-
-  double SeriesFrameRendererFactory::GetCurrentSliceThickness() const
-  {
-    if (currentDataset_.get() == NULL)
-    {
-      // There was no previous call "ReadCurrentFrameDataset()"
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-    }
-    
-    try
-    {
-      OrthancPlugins::DicomDatasetReader reader(*currentDataset_);
-
-      double thickness;
-      if (reader.GetDoubleValue(thickness, OrthancPlugins::DICOM_TAG_SLICE_THICKNESS))
-      {
-        return thickness;
-      }
-    }
-    catch (ORTHANC_PLUGINS_EXCEPTION_CLASS& e)
-    {
-    }
-
-    // Some arbitrary large slice thickness
-    return std::numeric_limits<double>::infinity();
-  }
-
-
-  SeriesFrameRendererFactory::SeriesFrameRendererFactory(ISeriesLoader* loader,   // Takes ownership
-                                                         bool fast) :
-    loader_(loader),
-    currentFrame_(0),
-    fast_(fast)
-  {
-    if (loader == NULL)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-    }
-  }
-
-
-  bool SeriesFrameRendererFactory::GetExtent(double& x1,
-                                             double& y1,
-                                             double& x2,
-                                             double& y2,
-                                             const SliceGeometry& viewportSlice)
-  {
-    if (currentDataset_.get() == NULL)
-    {
-      // There has been no previous call to
-      // "CreateLayerRenderer". Read some arbitrary DICOM frame, the
-      // one at the middle of the series.
-      unsigned int depth = loader_->GetGeometry().GetSliceCount();
-      ReadCurrentFrameDataset(depth / 2);
-    }
-
-    double spacingX, spacingY;
-    GetCurrentPixelSpacing(spacingX, spacingY);
-
-    return FrameRenderer::ComputeFrameExtent(x1, y1, x2, y2, 
-                                             viewportSlice, 
-                                             loader_->GetGeometry().GetSlice(0), 
-                                             loader_->GetWidth(), 
-                                             loader_->GetHeight(),
-                                             spacingX, spacingY);
-  }
-
-
-  ILayerRenderer* SeriesFrameRendererFactory::CreateLayerRenderer(const SliceGeometry& viewportSlice)
-  {
-    size_t closest;
-    double distance;
-
-    bool isOpposite;
-    if (!GeometryToolbox::IsParallelOrOpposite(isOpposite, loader_->GetGeometry().GetNormal(), viewportSlice.GetNormal()) ||
-        !loader_->GetGeometry().ComputeClosestSlice(closest, distance, viewportSlice.GetOrigin()))
-    {
-      // Unable to compute the slice in the series that is the
-      // closest to the slice displayed by the viewport
-      return NULL;
-    }
-
-    ReadCurrentFrameDataset(closest);
-    assert(currentDataset_.get() != NULL);
-        
-    double spacingX, spacingY;
-    GetCurrentPixelSpacing(spacingX, spacingY);
-
-    if (distance <= GetCurrentSliceThickness() / 2.0)
-    {
-      SliceGeometry frameSlice(*currentDataset_);
-      return FrameRenderer::CreateRenderer(loader_->DownloadFrame(closest), 
-                                           frameSlice,
-                                           *currentDataset_, 
-                                           spacingX, spacingY,
-                                           true);
-    }
-    else
-    {
-      // The closest slice of the series is too far away from the
-      // slice displayed by the viewport
-      return NULL;
-    }
-  }
-
-
-  ISliceableVolume& SeriesFrameRendererFactory::GetSourceVolume() const
-  {
-    throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-  }
-}
--- a/Framework/Layers/SeriesFrameRendererFactory.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,65 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "ILayerRendererFactory.h"
-
-#include "../Toolbox/ISeriesLoader.h"
-
-namespace OrthancStone
-{
-  class SeriesFrameRendererFactory : public ILayerRendererFactory
-  {
-  private:
-    std::auto_ptr<ISeriesLoader>  loader_;
-    size_t                        currentFrame_;
-    bool                          fast_;
-
-    std::auto_ptr<OrthancPlugins::IDicomDataset>  currentDataset_;
-
-    void ReadCurrentFrameDataset(size_t frame);
-
-    void GetCurrentPixelSpacing(double& spacingX,
-                                double& spacingY) const;
-
-    double GetCurrentSliceThickness() const;
-
-  public:
-    SeriesFrameRendererFactory(ISeriesLoader* loader,   // Takes ownership
-                               bool fast);
-
-    virtual bool GetExtent(double& x1,
-                           double& y1,
-                           double& x2,
-                           double& y2,
-                           const SliceGeometry& viewportSlice);
-
-    virtual ILayerRenderer* CreateLayerRenderer(const SliceGeometry& viewportSlice);
-
-    virtual bool HasSourceVolume() const
-    {
-      return false;
-    }
-
-    virtual ISliceableVolume& GetSourceVolume() const;
-  };
-}
--- a/Framework/Layers/SingleFrameRendererFactory.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,88 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "SingleFrameRendererFactory.h"
-
-#include "FrameRenderer.h"
-#include "../Toolbox/MessagingToolbox.h"
-#include "../Toolbox/DicomFrameConverter.h"
-
-#include <OrthancException.h>
-#include <Plugins/Samples/Common/FullOrthancDataset.h>
-#include <Plugins/Samples/Common/DicomDatasetReader.h>
-
-namespace OrthancStone
-{
-  SingleFrameRendererFactory::SingleFrameRendererFactory(OrthancPlugins::IOrthancConnection& orthanc,
-                                                         const std::string& instanceId,
-                                                         unsigned int frame) :
-    orthanc_(orthanc),
-    instance_(instanceId),
-    frame_(frame)
-  {
-    dicom_.reset(new OrthancPlugins::FullOrthancDataset(orthanc, "/instances/" + instanceId + "/tags"));
-
-    DicomFrameConverter converter;
-    converter.ReadParameters(*dicom_);
-    format_ = converter.GetExpectedPixelFormat();
-  }
-
-
-  bool SingleFrameRendererFactory::GetExtent(double& x1,
-                                             double& y1,
-                                             double& x2,
-                                             double& y2,
-                                             const SliceGeometry& viewportSlice)
-  {
-    // Assume that PixelSpacingX == PixelSpacingY == 1
-
-    OrthancPlugins::DicomDatasetReader reader(*dicom_);
-
-    unsigned int width, height;
-
-    if (!reader.GetUnsignedIntegerValue(width, OrthancPlugins::DICOM_TAG_COLUMNS) ||
-        !reader.GetUnsignedIntegerValue(height, OrthancPlugins::DICOM_TAG_ROWS))
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-    }
-
-    x1 = 0;
-    y1 = 0;
-    x2 = static_cast<double>(width);
-    y2 = static_cast<double>(height);
-
-    return true;
-  }
-
-
-  ILayerRenderer* SingleFrameRendererFactory::CreateLayerRenderer(const SliceGeometry& viewportSlice)
-  {
-    SliceGeometry frameSlice(*dicom_);
-    return FrameRenderer::CreateRenderer(MessagingToolbox::DecodeFrame(orthanc_, instance_, frame_, format_), 
-                                         frameSlice, *dicom_, 1, 1, true);
-  }
-
-
-  ISliceableVolume& SingleFrameRendererFactory::GetSourceVolume() const
-  {
-    throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-  }
-}
--- a/Framework/Layers/SingleFrameRendererFactory.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "ILayerRendererFactory.h"
-#include <Plugins/Samples/Common/IOrthancConnection.h>
-
-namespace OrthancStone
-{
-  class SingleFrameRendererFactory : public ILayerRendererFactory
-  {
-  private:
-    OrthancPlugins::IOrthancConnection&           orthanc_;
-    std::auto_ptr<OrthancPlugins::IDicomDataset>  dicom_;
-
-    std::string           instance_;
-    unsigned int          frame_;
-    Orthanc::PixelFormat  format_;
-
-  public:
-    SingleFrameRendererFactory(OrthancPlugins::IOrthancConnection& orthanc,
-                               const std::string& instanceId,
-                               unsigned int frame);
-
-    const OrthancPlugins::IDicomDataset& GetDataset() const
-    {
-      return *dicom_;
-    }
-
-    SliceGeometry GetSliceGeometry()
-    {
-      return SliceGeometry(*dicom_);
-    }
-
-    virtual bool GetExtent(double& x1,
-                           double& y1,
-                           double& x2,
-                           double& y2,
-                           const SliceGeometry& viewportSlice);
-
-    virtual ILayerRenderer* CreateLayerRenderer(const SliceGeometry& viewportSlice);
-
-    virtual bool HasSourceVolume() const
-    {
-      return false;
-    }
-
-    virtual ISliceableVolume& GetSourceVolume() const;
-  };
-}
--- a/Framework/Layers/SliceOutlineRenderer.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "SliceOutlineRenderer.h"
-
-namespace OrthancStone
-{
-  bool SliceOutlineRenderer::RenderLayer(CairoContext& context,
-                                         const ViewportGeometry& view)
-  {
-    if (style_.visible_)
-    {
-      cairo_t *cr = context.GetObject();
-      cairo_save(cr);
-
-      context.SetSourceColor(style_.drawColor_);
-
-      double x1 = -0.5 * pixelSpacingX_;
-      double y1 = -0.5 * pixelSpacingY_;
-        
-      cairo_set_line_width(cr, 1.0 / view.GetZoom());
-      cairo_rectangle(cr, x1, y1,
-                      static_cast<double>(width_) * pixelSpacingX_,
-                      static_cast<double>(height_) * pixelSpacingY_);
-
-      double handleSize = 10.0f / view.GetZoom();
-      cairo_move_to(cr, x1 + handleSize, y1);
-      cairo_line_to(cr, x1, y1 + handleSize);
-
-      cairo_stroke(cr);
-      cairo_restore(cr);
-    }
-
-    return true;
-  }
-}
--- a/Framework/Layers/SliceOutlineRenderer.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "ILayerRenderer.h"
-#include "../Toolbox/Slice.h"
-
-namespace OrthancStone
-{
-  class SliceOutlineRenderer : public ILayerRenderer
-  {
-  private:
-    CoordinateSystem3D  geometry_;
-    double              pixelSpacingX_;
-    double              pixelSpacingY_;
-    unsigned int        width_;
-    unsigned int        height_;
-    RenderStyle         style_;
-
-  public:
-    SliceOutlineRenderer(const Deprecated::Slice& slice) :
-      geometry_(slice.GetGeometry()),
-      pixelSpacingX_(slice.GetPixelSpacingX()),
-      pixelSpacingY_(slice.GetPixelSpacingY()),
-      width_(slice.GetWidth()),
-      height_(slice.GetHeight())
-    {
-    }
-
-    virtual bool RenderLayer(CairoContext& context,
-                             const ViewportGeometry& view);
-
-    virtual void SetLayerStyle(const RenderStyle& style)
-    {
-      style_ = style;
-    }
-
-    virtual const CoordinateSystem3D& GetLayerSlice()
-    {
-      return geometry_;
-    }
-
-    virtual bool IsFullQuality()
-    {
-      return true;
-    }
-  };
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Messages/IMessageEmitter.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,39 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "IObserver.h"
+#include "IMessage.h"
+
+namespace OrthancStone
+{
+  class IMessageEmitter : public boost::noncopyable
+  {
+  public:
+    virtual ~IMessageEmitter()
+    {
+    }
+
+    virtual void EmitMessage(const IObserver& observer,
+                             const IMessage& message) = 0;
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Oracle/GetOrthancImageCommand.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,153 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "GetOrthancImageCommand.h"
+
+#include <Core/Images/JpegReader.h>
+#include <Core/Images/PamReader.h>
+#include <Core/Images/PngReader.h>
+#include <Core/OrthancException.h>
+#include <Core/Toolbox.h>
+
+namespace OrthancStone
+{
+  GetOrthancImageCommand::SuccessMessage::SuccessMessage(const GetOrthancImageCommand& command,
+                                                         Orthanc::ImageAccessor* image,   // Takes ownership
+                                                         Orthanc::MimeType mime) :
+    OriginMessage(command),
+    image_(image),
+    mime_(mime)
+  {
+    if (image == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+    }
+  }
+
+
+  GetOrthancImageCommand::GetOrthancImageCommand() :
+    uri_("/"),
+    timeout_(10),
+    hasExpectedFormat_(false)
+  {
+  }
+
+
+  void GetOrthancImageCommand::SetExpectedPixelFormat(Orthanc::PixelFormat format)
+  {
+    hasExpectedFormat_ = true;
+    expectedFormat_ = format;
+  }
+
+
+  void GetOrthancImageCommand::SetInstanceUri(const std::string& instance,
+                                              Orthanc::PixelFormat pixelFormat)
+  {
+    uri_ = "/instances/" + instance;
+          
+    switch (pixelFormat)
+    {
+      case Orthanc::PixelFormat_RGB24:
+        uri_ += "/preview";
+        break;
+      
+      case Orthanc::PixelFormat_Grayscale16:
+        uri_ += "/image-uint16";
+        break;
+      
+      case Orthanc::PixelFormat_SignedGrayscale16:
+        uri_ += "/image-int16";
+        break;
+      
+      default:
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+  }
+
+  void GetOrthancImageCommand::ProcessHttpAnswer(IMessageEmitter& emitter,
+                                                 const IObserver& receiver,
+                                                 const std::string& answer,
+                                                 const HttpHeaders& answerHeaders) const
+  {
+    Orthanc::MimeType contentType = Orthanc::MimeType_Binary;
+
+    for (HttpHeaders::const_iterator it = answerHeaders.begin(); 
+         it != answerHeaders.end(); ++it)
+    {
+      std::string s;
+      Orthanc::Toolbox::ToLowerCase(s, it->first);
+
+      if (s == "content-type")
+      {
+        contentType = Orthanc::StringToMimeType(it->second);
+        break;
+      }
+    }
+
+    std::auto_ptr<Orthanc::ImageAccessor> image;
+
+    switch (contentType)
+    {
+      case Orthanc::MimeType_Png:
+      {
+        image.reset(new Orthanc::PngReader);
+        dynamic_cast<Orthanc::PngReader&>(*image).ReadFromMemory(answer);
+        break;
+      }
+
+      case Orthanc::MimeType_Pam:
+      {
+        image.reset(new Orthanc::PamReader);
+        dynamic_cast<Orthanc::PamReader&>(*image).ReadFromMemory(answer);
+        break;
+      }
+
+      case Orthanc::MimeType_Jpeg:
+      {
+        image.reset(new Orthanc::JpegReader);
+        dynamic_cast<Orthanc::JpegReader&>(*image).ReadFromMemory(answer);
+        break;
+      }
+
+      default:
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol,
+                                        "Unsupported HTTP Content-Type for an image: " + 
+                                        std::string(Orthanc::EnumerationToString(contentType)));
+    }
+
+    if (hasExpectedFormat_)
+    {
+      if (expectedFormat_ == Orthanc::PixelFormat_SignedGrayscale16 &&
+          image->GetFormat() == Orthanc::PixelFormat_Grayscale16)
+      {
+        image->SetFormat(Orthanc::PixelFormat_SignedGrayscale16);
+      }
+
+      if (expectedFormat_ != image->GetFormat())
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
+      }
+    }
+
+    SuccessMessage message(*this, image.release(), contentType);
+    emitter.EmitMessage(receiver, message);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Oracle/GetOrthancImageCommand.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,119 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../Messages/IMessageEmitter.h"
+#include "OracleCommandWithPayload.h"
+
+#include <Core/Images/ImageAccessor.h>
+
+#include <map>
+
+namespace OrthancStone
+{
+  class GetOrthancImageCommand : public OracleCommandWithPayload
+  {
+  public:
+    typedef std::map<std::string, std::string>  HttpHeaders;
+
+    class SuccessMessage : public OriginMessage<GetOrthancImageCommand>
+    {
+      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
+      
+    private:
+      std::auto_ptr<Orthanc::ImageAccessor>  image_;
+      Orthanc::MimeType                      mime_;
+
+    public:
+      SuccessMessage(const GetOrthancImageCommand& command,
+                     Orthanc::ImageAccessor* image,   // Takes ownership
+                     Orthanc::MimeType mime);
+
+      const Orthanc::ImageAccessor& GetImage() const
+      {
+        return *image_;
+      }
+
+      Orthanc::MimeType GetMimeType() const
+      {
+        return mime_;
+      }
+    };
+
+
+  private:
+    std::string           uri_;
+    HttpHeaders           headers_;
+    unsigned int          timeout_;
+    bool                  hasExpectedFormat_;
+    Orthanc::PixelFormat  expectedFormat_;
+
+  public:
+    GetOrthancImageCommand();
+
+    virtual Type GetType() const
+    {
+      return Type_GetOrthancImage;
+    }
+
+    void SetExpectedPixelFormat(Orthanc::PixelFormat format);
+
+    void SetUri(const std::string& uri)
+    {
+      uri_ = uri;
+    }
+
+    void SetInstanceUri(const std::string& instance,
+                        Orthanc::PixelFormat pixelFormat);
+
+    void SetHttpHeader(const std::string& key,
+                       const std::string& value)
+    {
+      headers_[key] = value;
+    }
+
+    const std::string& GetUri() const
+    {
+      return uri_;
+    }
+
+    const HttpHeaders& GetHttpHeaders() const
+    {
+      return headers_;
+    }
+
+    void SetTimeout(unsigned int seconds)
+    {
+      timeout_ = seconds;
+    }
+
+    unsigned int GetTimeout() const
+    {
+      return timeout_;
+    }
+
+    void ProcessHttpAnswer(IMessageEmitter& emitter,
+                           const IObserver& receiver,
+                           const std::string& answer,
+                           const HttpHeaders& answerHeaders) const;
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Oracle/GetOrthancWebViewerJpegCommand.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,217 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "GetOrthancWebViewerJpegCommand.h"
+
+#include "../Toolbox/LinearAlgebra.h"
+
+#include <Core/Images/Image.h>
+#include <Core/Images/ImageProcessing.h>
+#include <Core/Images/JpegReader.h>
+#include <Core/OrthancException.h>
+#include <Core/Toolbox.h>
+
+#include <json/reader.h>
+#include <json/value.h>
+
+namespace OrthancStone
+{
+  GetOrthancWebViewerJpegCommand::SuccessMessage::SuccessMessage(const GetOrthancWebViewerJpegCommand& command,
+                                                                 Orthanc::ImageAccessor* image) :   // Takes ownership
+    OriginMessage(command),
+    image_(image)
+  {
+    if (image == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+    }
+  }
+
+
+  GetOrthancWebViewerJpegCommand::GetOrthancWebViewerJpegCommand() :
+    frame_(0),
+    quality_(95),
+    timeout_(10),
+    expectedFormat_(Orthanc::PixelFormat_Grayscale8)
+  {
+  }
+
+
+  void GetOrthancWebViewerJpegCommand::SetQuality(unsigned int quality)
+  {
+    if (quality <= 0 ||
+        quality > 100)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+    else
+    {
+      quality_ = quality;
+    }
+  }
+
+
+  std::string GetOrthancWebViewerJpegCommand::GetUri() const
+  {
+    return ("/web-viewer/instances/jpeg" + boost::lexical_cast<std::string>(quality_) +
+            "-" + instanceId_ + "_" + boost::lexical_cast<std::string>(frame_));
+  }
+
+
+  void GetOrthancWebViewerJpegCommand::ProcessHttpAnswer(IMessageEmitter& emitter,
+                                                         const IObserver& receiver,
+                                                         const std::string& answer) const
+  {
+    // This code comes from older "OrthancSlicesLoader::ParseSliceImageJpeg()"
+      
+    Json::Value encoded;
+
+    {
+      Json::Reader reader;
+      if (!reader.parse(answer, encoded))
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+      }
+    }
+
+    if (encoded.type() != Json::objectValue ||
+        !encoded.isMember("Orthanc") ||
+        encoded["Orthanc"].type() != Json::objectValue)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+    }
+    
+    const Json::Value& info = encoded["Orthanc"];
+    if (!info.isMember("PixelData") ||
+        !info.isMember("Stretched") ||
+        !info.isMember("Compression") ||
+        info["Compression"].type() != Json::stringValue ||
+        info["PixelData"].type() != Json::stringValue ||
+        info["Stretched"].type() != Json::booleanValue ||
+        info["Compression"].asString() != "Jpeg")
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+    }
+    
+    bool isSigned = false;
+    bool isStretched = info["Stretched"].asBool();
+    
+    if (info.isMember("IsSigned"))
+    {
+      if (info["IsSigned"].type() != Json::booleanValue)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+      }
+      else
+      {
+        isSigned = info["IsSigned"].asBool();
+      }
+    }
+    
+    std::auto_ptr<Orthanc::ImageAccessor> reader;
+    
+    {
+      std::string jpeg;
+      Orthanc::Toolbox::DecodeBase64(jpeg, info["PixelData"].asString());
+      
+      reader.reset(new Orthanc::JpegReader);
+      dynamic_cast<Orthanc::JpegReader&>(*reader).ReadFromMemory(jpeg);
+    }
+    
+    if (reader->GetFormat() == Orthanc::PixelFormat_RGB24)  // This is a color image
+    {
+      if (expectedFormat_ != Orthanc::PixelFormat_RGB24)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+      }
+      
+      if (isSigned || isStretched)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+      }
+      else
+      {
+        SuccessMessage message(*this, reader.release());
+        emitter.EmitMessage(receiver, message);
+        return;
+      }
+    }
+    
+    if (reader->GetFormat() != Orthanc::PixelFormat_Grayscale8)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+    }
+    
+    if (!isStretched)
+    {
+      if (expectedFormat_ != reader->GetFormat())
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+      }
+      else
+      {
+        SuccessMessage message(*this, reader.release());
+        emitter.EmitMessage(receiver, message);
+        return;
+      }
+    }
+    
+    int32_t stretchLow = 0;
+    int32_t stretchHigh = 0;
+    
+    if (!info.isMember("StretchLow") ||
+        !info.isMember("StretchHigh") ||
+        info["StretchLow"].type() != Json::intValue ||
+        info["StretchHigh"].type() != Json::intValue)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+    }
+    
+    stretchLow = info["StretchLow"].asInt();
+    stretchHigh = info["StretchHigh"].asInt();
+    
+    if (stretchLow < -32768 ||
+        stretchHigh > 65535 ||
+        (stretchLow < 0 && stretchHigh > 32767))
+    {
+      // This range cannot be represented with a uint16_t or an int16_t
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+    }
+    
+    // Decode a grayscale JPEG 8bpp image coming from the Web viewer
+    std::auto_ptr<Orthanc::ImageAccessor> image
+      (new Orthanc::Image(expectedFormat_, reader->GetWidth(), reader->GetHeight(), false));
+
+    Orthanc::ImageProcessing::Convert(*image, *reader);
+    reader.reset();
+    
+    float scaling = static_cast<float>(stretchHigh - stretchLow) / 255.0f;
+    
+    if (!LinearAlgebra::IsCloseToZero(scaling))
+    {
+      float offset = static_cast<float>(stretchLow) / scaling;
+      Orthanc::ImageProcessing::ShiftScale(*image, offset, scaling, true);
+    }
+    
+    SuccessMessage message(*this, image.release());
+    emitter.EmitMessage(receiver, message);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Oracle/GetOrthancWebViewerJpegCommand.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,135 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../Messages/IMessageEmitter.h"
+#include "OracleCommandWithPayload.h"
+
+#include <Core/Images/ImageAccessor.h>
+
+#include <map>
+
+namespace OrthancStone
+{
+  class GetOrthancWebViewerJpegCommand : public OracleCommandWithPayload
+  {
+  public:
+    typedef std::map<std::string, std::string>  HttpHeaders;
+
+    class SuccessMessage : public OriginMessage<GetOrthancWebViewerJpegCommand>
+    {
+      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
+      
+    private:
+      std::auto_ptr<Orthanc::ImageAccessor>  image_;
+
+    public:
+      SuccessMessage(const GetOrthancWebViewerJpegCommand& command,
+                     Orthanc::ImageAccessor* image);   // Takes ownership
+
+      const Orthanc::ImageAccessor& GetImage() const
+      {
+        return *image_;
+      }
+    };
+
+  private:
+    std::string           instanceId_;
+    unsigned int          frame_;
+    unsigned int          quality_;
+    HttpHeaders           headers_;
+    unsigned int          timeout_;
+    Orthanc::PixelFormat  expectedFormat_;
+
+  public:
+    GetOrthancWebViewerJpegCommand();
+
+    virtual Type GetType() const
+    {
+      return Type_GetOrthancWebViewerJpeg;
+    }
+
+    void SetExpectedPixelFormat(Orthanc::PixelFormat format)
+    {
+      expectedFormat_ = format;
+    }
+
+    void SetInstance(const std::string& instanceId)
+    {
+      instanceId_ = instanceId;
+    }
+
+    void SetFrame(unsigned int frame)
+    {
+      frame_ = frame;
+    }
+
+    void SetQuality(unsigned int quality);
+
+    void SetHttpHeader(const std::string& key,
+                       const std::string& value)
+    {
+      headers_[key] = value;
+    }
+
+    Orthanc::PixelFormat GetExpectedPixelFormat() const
+    {
+      return expectedFormat_;
+    }
+
+    const std::string& GetInstanceId() const
+    {
+      return instanceId_;
+    }
+
+    unsigned int GetFrame() const
+    {
+      return frame_;
+    }
+
+    unsigned int GetQuality() const
+    {
+      return quality_;
+    }
+
+    const HttpHeaders& GetHttpHeaders() const
+    {
+      return headers_;
+    }
+
+    void SetTimeout(unsigned int seconds)
+    {
+      timeout_ = seconds;
+    }
+
+    unsigned int GetTimeout() const
+    {
+      return timeout_;
+    }
+
+    std::string GetUri() const;
+
+    void ProcessHttpAnswer(IMessageEmitter& emitter,
+                           const IObserver& receiver,
+                           const std::string& answer) const;
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Oracle/IOracle.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,38 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "IOracleCommand.h"
+
+namespace OrthancStone
+{
+  class IOracle : public boost::noncopyable
+  {
+  public:
+    virtual ~IOracle()
+    {
+    }
+
+    virtual void Schedule(const IObserver& receiver,
+                          IOracleCommand* command) = 0;  // Takes ownership
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Oracle/IOracleCommand.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,45 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include <boost/noncopyable.hpp>
+
+namespace OrthancStone
+{
+  class IOracleCommand : public boost::noncopyable
+  {
+  public:
+    enum Type
+    {
+      Type_Sleep,
+      Type_OrthancRestApi,
+      Type_GetOrthancImage,
+      Type_GetOrthancWebViewerJpeg
+    };
+
+    virtual ~IOracleCommand()
+    {
+    }
+
+    virtual Type GetType() const = 0;
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Oracle/OracleCommandExceptionMessage.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,64 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../Messages/IMessage.h"
+#include "IOracleCommand.h"
+
+#include <Core/OrthancException.h>
+
+namespace OrthancStone
+{
+  class OracleCommandExceptionMessage : public IMessage
+  {
+    ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
+
+  private:
+    const IOracleCommand&       command_;
+    Orthanc::OrthancException   exception_;
+
+  public:
+    OracleCommandExceptionMessage(const IOracleCommand& command,
+                                  const Orthanc::OrthancException& exception) :
+      command_(command),
+      exception_(exception)
+    {
+    }
+
+    OracleCommandExceptionMessage(const IOracleCommand& command,
+                                  const Orthanc::ErrorCode& error) :
+      command_(command),
+      exception_(error)
+    {
+    }
+
+    const IOracleCommand& GetCommand() const
+    {
+      return command_;
+    }
+    
+    const Orthanc::OrthancException& GetException() const
+    {
+      return exception_;
+    }
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Oracle/OracleCommandWithPayload.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,65 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "OracleCommandWithPayload.h"
+
+#include <Core/OrthancException.h>
+
+namespace OrthancStone
+{
+  void OracleCommandWithPayload::SetPayload(Orthanc::IDynamicObject* payload)
+  {
+    if (payload == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+    }
+    else
+    {
+      payload_.reset(payload);
+    }    
+  }
+
+
+  const Orthanc::IDynamicObject& OracleCommandWithPayload::GetPayload() const
+  {
+    if (HasPayload())
+    {
+      return *payload_;
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+  }
+
+
+  Orthanc::IDynamicObject* OracleCommandWithPayload::ReleasePayload()
+  {
+    if (HasPayload())
+    {
+      return payload_.release();
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Oracle/OracleCommandWithPayload.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,49 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "IOracleCommand.h"
+
+#include <Core/IDynamicObject.h>
+
+#include <memory>
+
+namespace OrthancStone
+{
+  class OracleCommandWithPayload : public IOracleCommand
+  {
+  private:
+    std::auto_ptr<Orthanc::IDynamicObject>  payload_;
+
+  public:
+    void SetPayload(Orthanc::IDynamicObject* payload);
+
+    bool HasPayload() const
+    {
+      return (payload_.get() != NULL);
+    }
+
+    const Orthanc::IDynamicObject& GetPayload() const;
+
+    Orthanc::IDynamicObject* ReleasePayload();
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Oracle/OrthancRestApiCommand.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,78 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "OrthancRestApiCommand.h"
+
+#include <Core/OrthancException.h>
+
+#include <json/reader.h>
+#include <json/writer.h>
+
+namespace OrthancStone
+{
+  OrthancRestApiCommand::SuccessMessage::SuccessMessage(const OrthancRestApiCommand& command,
+                                                        const HttpHeaders& answerHeaders,
+                                                        std::string& answer) :
+    OriginMessage(command),
+    headers_(answerHeaders),
+    answer_(answer)
+  {
+  }
+
+
+  void OrthancRestApiCommand::SuccessMessage::ParseJsonBody(Json::Value& target) const
+  {
+    Json::Reader reader;
+    if (!reader.parse(answer_, target))
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+    }
+  }
+
+
+  OrthancRestApiCommand::OrthancRestApiCommand() :
+    method_(Orthanc::HttpMethod_Get),
+    uri_("/"),
+    timeout_(10)
+  {
+  }
+
+
+  void OrthancRestApiCommand::SetBody(const Json::Value& json)
+  {
+    Json::FastWriter writer;
+    body_ = writer.write(json);
+  }
+
+
+  const std::string& OrthancRestApiCommand::GetBody() const
+  {
+    if (method_ == Orthanc::HttpMethod_Post ||
+        method_ == Orthanc::HttpMethod_Put)
+    {
+      return body_;
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Oracle/OrthancRestApiCommand.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,131 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../Messages/IMessage.h"
+#include "OracleCommandWithPayload.h"
+
+#include <Core/Enumerations.h>
+
+#include <map>
+#include <json/value.h>
+
+namespace OrthancStone
+{
+  class OrthancRestApiCommand : public OracleCommandWithPayload
+  {
+  public:
+    typedef std::map<std::string, std::string>  HttpHeaders;
+
+    class SuccessMessage : public OriginMessage<OrthancRestApiCommand>
+    {
+      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
+      
+    private:
+      HttpHeaders   headers_;
+      std::string   answer_;
+
+    public:
+      SuccessMessage(const OrthancRestApiCommand& command,
+                     const HttpHeaders& answerHeaders,
+                     std::string& answer  /* will be swapped to avoid a memcpy() */);
+
+      const std::string& GetAnswer() const
+      {
+        return answer_;
+      }
+
+      void ParseJsonBody(Json::Value& target) const;
+
+      const HttpHeaders&  GetAnswerHeaders() const
+      {
+        return headers_;
+      }
+    };
+
+
+  private:
+    Orthanc::HttpMethod  method_;
+    std::string          uri_;
+    std::string          body_;
+    HttpHeaders          headers_;
+    unsigned int         timeout_;
+
+  public:
+    OrthancRestApiCommand();
+
+    virtual Type GetType() const
+    {
+      return Type_OrthancRestApi;
+    }
+
+    void SetMethod(Orthanc::HttpMethod method)
+    {
+      method_ = method;
+    }
+
+    void SetUri(const std::string& uri)
+    {
+      uri_ = uri;
+    }
+
+    void SetBody(const std::string& body)
+    {
+      body_ = body;
+    }
+
+    void SetBody(const Json::Value& json);
+
+    void SetHttpHeader(const std::string& key,
+                       const std::string& value)
+    {
+      headers_[key] = value;
+    }
+
+    Orthanc::HttpMethod GetMethod() const
+    {
+      return method_;
+    }
+
+    const std::string& GetUri() const
+    {
+      return uri_;
+    }
+
+    const std::string& GetBody() const;
+
+    const HttpHeaders& GetHttpHeaders() const
+    {
+      return headers_;
+    }
+
+    void SetTimeout(unsigned int seconds)
+    {
+      timeout_ = seconds;
+    }
+
+    unsigned int GetTimeout() const
+    {
+      return timeout_;
+    }
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Oracle/SleepOracleCommand.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,52 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../Messages/IMessage.h"
+#include "OracleCommandWithPayload.h"
+
+namespace OrthancStone
+{
+  class SleepOracleCommand : public OracleCommandWithPayload
+  {
+  private:
+    unsigned int  milliseconds_;
+
+  public:
+    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, TimeoutMessage, SleepOracleCommand);
+
+    SleepOracleCommand(unsigned int milliseconds) : 
+    milliseconds_(milliseconds)
+    {
+    }
+
+    virtual Type GetType() const
+    {
+      return Type_Sleep;
+    }
+
+    unsigned int GetDelay() const
+    {
+      return milliseconds_;
+    }
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Oracle/ThreadedOracle.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,507 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "ThreadedOracle.h"
+
+#include "GetOrthancImageCommand.h"
+#include "GetOrthancWebViewerJpegCommand.h"
+#include "OrthancRestApiCommand.h"
+#include "SleepOracleCommand.h"
+#include "OracleCommandExceptionMessage.h"
+
+#include <Core/Compression/GzipCompressor.h>
+#include <Core/HttpClient.h>
+#include <Core/OrthancException.h>
+#include <Core/Toolbox.h>
+
+
+namespace OrthancStone
+{
+  class ThreadedOracle::Item : public Orthanc::IDynamicObject
+  {
+  private:
+    const IObserver&                receiver_;
+    std::auto_ptr<IOracleCommand>   command_;
+
+  public:
+    Item(const IObserver& receiver,
+         IOracleCommand* command) :
+      receiver_(receiver),
+      command_(command)
+    {
+      if (command == NULL)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+      }
+    }
+
+    const IObserver& GetReceiver() const
+    {
+      return receiver_;
+    }
+
+    IOracleCommand& GetCommand()
+    {
+      assert(command_.get() != NULL);
+      return *command_;
+    }
+  };
+
+
+  class ThreadedOracle::SleepingCommands : public boost::noncopyable
+  {
+  private:
+    class Item
+    {
+    private:
+      const IObserver&                   receiver_;
+      std::auto_ptr<SleepOracleCommand>  command_;
+      boost::posix_time::ptime           expiration_;
+
+    public:
+      Item(const IObserver& receiver,
+           SleepOracleCommand* command) :
+        receiver_(receiver),
+        command_(command)
+      {
+        if (command == NULL)
+        {
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+        }
+
+        expiration_ = (boost::posix_time::second_clock::local_time() + 
+                       boost::posix_time::milliseconds(command_->GetDelay()));
+      }
+
+      const boost::posix_time::ptime& GetExpirationTime() const
+      {
+        return expiration_;
+      }
+
+      void Awake(IMessageEmitter& emitter)
+      {
+        assert(command_.get() != NULL);
+
+        SleepOracleCommand::TimeoutMessage message(*command_);
+        emitter.EmitMessage(receiver_, message);
+      }
+    };
+
+    typedef std::list<Item*>  Content;
+
+    boost::mutex  mutex_;
+    Content       content_;
+
+  public:
+    ~SleepingCommands()
+    {
+      for (Content::iterator it = content_.begin(); it != content_.end(); ++it)
+      {
+        if (*it != NULL)
+        {
+          delete *it;
+        }
+      }
+    }
+
+    void Add(const IObserver& receiver,
+             SleepOracleCommand* command)   // Takes ownership
+    {
+      boost::mutex::scoped_lock lock(mutex_);
+
+      content_.push_back(new Item(receiver, command));
+    }
+
+    void AwakeExpired(IMessageEmitter& emitter)
+    {
+      boost::mutex::scoped_lock lock(mutex_);
+
+      const boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
+
+      Content  stillSleeping;
+        
+      for (Content::iterator it = content_.begin(); it != content_.end(); ++it)
+      {
+        if (*it != NULL &&
+            (*it)->GetExpirationTime() <= now)
+        {
+          (*it)->Awake(emitter);
+          delete *it;
+          *it = NULL;
+        }
+        else
+        {
+          stillSleeping.push_back(*it);
+        }
+      }
+
+      // Compact the still-sleeping commands
+      content_ = stillSleeping;
+    }
+  };
+
+
+  static void CopyHttpHeaders(Orthanc::HttpClient& client,
+                              const Orthanc::HttpClient::HttpHeaders& headers)
+  {
+    for (Orthanc::HttpClient::HttpHeaders::const_iterator
+           it = headers.begin(); it != headers.end(); it++ )
+    {
+      client.AddHeader(it->first, it->second);
+    }
+  }
+
+
+  static void DecodeAnswer(std::string& answer,
+                           const Orthanc::HttpClient::HttpHeaders& headers)
+  {
+    Orthanc::HttpCompression contentEncoding = Orthanc::HttpCompression_None;
+
+    for (Orthanc::HttpClient::HttpHeaders::const_iterator it = headers.begin(); 
+         it != headers.end(); ++it)
+    {
+      std::string s;
+      Orthanc::Toolbox::ToLowerCase(s, it->first);
+
+      if (s == "content-encoding")
+      {
+        if (it->second == "gzip")
+        {
+          contentEncoding = Orthanc::HttpCompression_Gzip;
+        }
+        else 
+        {
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol,
+                                          "Unsupported HTTP Content-Encoding: " + it->second);
+        }
+
+        break;
+      }
+    }
+
+    if (contentEncoding == Orthanc::HttpCompression_Gzip)
+    {
+      std::string compressed;
+      answer.swap(compressed);
+          
+      Orthanc::GzipCompressor compressor;
+      compressor.Uncompress(answer, compressed.c_str(), compressed.size());
+    }
+  }
+
+
+  static void Execute(IMessageEmitter& emitter,
+                      const Orthanc::WebServiceParameters& orthanc,
+                      const IObserver& receiver,
+                      const OrthancRestApiCommand& command)
+  {
+    Orthanc::HttpClient client(orthanc, command.GetUri());
+    client.SetMethod(command.GetMethod());
+    client.SetTimeout(command.GetTimeout());
+
+    CopyHttpHeaders(client, command.GetHttpHeaders());
+
+    if (command.GetMethod() == Orthanc::HttpMethod_Post ||
+        command.GetMethod() == Orthanc::HttpMethod_Put)
+    {
+      client.SetBody(command.GetBody());
+    }
+
+    std::string answer;
+    Orthanc::HttpClient::HttpHeaders answerHeaders;
+    client.ApplyAndThrowException(answer, answerHeaders);
+
+    DecodeAnswer(answer, answerHeaders);
+
+    OrthancRestApiCommand::SuccessMessage message(command, answerHeaders, answer);
+    emitter.EmitMessage(receiver, message);
+  }
+
+
+  static void Execute(IMessageEmitter& emitter,
+                      const Orthanc::WebServiceParameters& orthanc,
+                      const IObserver& receiver,
+                      const GetOrthancImageCommand& command)
+  {
+    Orthanc::HttpClient client(orthanc, command.GetUri());
+    client.SetTimeout(command.GetTimeout());
+
+    CopyHttpHeaders(client, command.GetHttpHeaders());
+
+    std::string answer;
+    Orthanc::HttpClient::HttpHeaders answerHeaders;
+    client.ApplyAndThrowException(answer, answerHeaders);
+
+    DecodeAnswer(answer, answerHeaders);
+
+    command.ProcessHttpAnswer(emitter, receiver, answer, answerHeaders);
+  }
+
+
+  static void Execute(IMessageEmitter& emitter,
+                      const Orthanc::WebServiceParameters& orthanc,
+                      const IObserver& receiver,
+                      const GetOrthancWebViewerJpegCommand& command)
+  {
+    Orthanc::HttpClient client(orthanc, command.GetUri());
+    client.SetTimeout(command.GetTimeout());
+
+    CopyHttpHeaders(client, command.GetHttpHeaders());
+
+    std::string answer;
+    Orthanc::HttpClient::HttpHeaders answerHeaders;
+    client.ApplyAndThrowException(answer, answerHeaders);
+
+    DecodeAnswer(answer, answerHeaders);
+
+    command.ProcessHttpAnswer(emitter, receiver, answer);
+  }
+
+
+  void ThreadedOracle::Step()
+  {
+    std::auto_ptr<Orthanc::IDynamicObject>  object(queue_.Dequeue(100));
+
+    if (object.get() != NULL)
+    {
+      Item& item = dynamic_cast<Item&>(*object);
+
+      try
+      {
+        switch (item.GetCommand().GetType())
+        {
+          case IOracleCommand::Type_Sleep:
+          {
+            SleepOracleCommand& command = dynamic_cast<SleepOracleCommand&>(item.GetCommand());
+
+            std::auto_ptr<SleepOracleCommand> copy(new SleepOracleCommand(command.GetDelay()));
+
+            if (command.HasPayload())
+            {
+              copy->SetPayload(command.ReleasePayload());
+            }
+
+            sleepingCommands_->Add(item.GetReceiver(), copy.release());
+
+            break;
+          }
+
+          case IOracleCommand::Type_OrthancRestApi:
+            Execute(emitter_, orthanc_, item.GetReceiver(), 
+                    dynamic_cast<const OrthancRestApiCommand&>(item.GetCommand()));
+            break;
+
+          case IOracleCommand::Type_GetOrthancImage:
+            Execute(emitter_, orthanc_, item.GetReceiver(), 
+                    dynamic_cast<const GetOrthancImageCommand&>(item.GetCommand()));
+            break;
+
+          case IOracleCommand::Type_GetOrthancWebViewerJpeg:
+            Execute(emitter_, orthanc_, item.GetReceiver(), 
+                    dynamic_cast<const GetOrthancWebViewerJpegCommand&>(item.GetCommand()));
+            break;
+
+          default:
+            throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+        }
+      }
+      catch (Orthanc::OrthancException& e)
+      {
+        LOG(ERROR) << "Exception within the oracle: " << e.What();
+        emitter_.EmitMessage(item.GetReceiver(), OracleCommandExceptionMessage(item.GetCommand(), e));
+      }
+      catch (...)
+      {
+        LOG(ERROR) << "Threaded exception within the oracle";
+        emitter_.EmitMessage(item.GetReceiver(), OracleCommandExceptionMessage
+                             (item.GetCommand(), Orthanc::ErrorCode_InternalError));
+      }
+    }
+  }
+
+
+  void ThreadedOracle::Worker(ThreadedOracle* that)
+  {
+    assert(that != NULL);
+      
+    for (;;)
+    {
+      {
+        boost::mutex::scoped_lock lock(that->mutex_);
+        if (that->state_ != State_Running)
+        {
+          return;
+        }
+      }
+
+      that->Step();
+    }
+  }
+
+
+  void ThreadedOracle::SleepingWorker(ThreadedOracle* that)
+  {
+    assert(that != NULL);
+      
+    for (;;)
+    {
+      {
+        boost::mutex::scoped_lock lock(that->mutex_);
+        if (that->state_ != State_Running)
+        {
+          return;
+        }
+      }
+
+      that->sleepingCommands_->AwakeExpired(that->emitter_);
+
+      boost::this_thread::sleep(boost::posix_time::milliseconds(that->sleepingTimeResolution_));
+    }
+  }
+
+
+  void ThreadedOracle::StopInternal()
+  {
+    {
+      boost::mutex::scoped_lock lock(mutex_);
+
+      if (state_ == State_Setup ||
+          state_ == State_Stopped)
+      {
+        return;
+      }
+      else
+      {
+        state_ = State_Stopped;
+      }
+    }
+
+    if (sleepingWorker_.joinable())
+    {
+      sleepingWorker_.join();
+    }
+
+    for (size_t i = 0; i < workers_.size(); i++)
+    {
+      if (workers_[i] != NULL)
+      {
+        if (workers_[i]->joinable())
+        {
+          workers_[i]->join();
+        }
+
+        delete workers_[i];
+      }
+    } 
+  }
+
+
+  ThreadedOracle::ThreadedOracle(IMessageEmitter& emitter) :
+    emitter_(emitter),
+    state_(State_Setup),
+    workers_(4),
+    sleepingCommands_(new SleepingCommands),
+    sleepingTimeResolution_(50)  // By default, time resolution of 50ms
+  {
+  }
+
+
+  void ThreadedOracle::SetOrthancParameters(const Orthanc::WebServiceParameters& orthanc)
+  {
+    boost::mutex::scoped_lock lock(mutex_);
+
+    if (state_ != State_Setup)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+    else
+    {
+      orthanc_ = orthanc;
+    }
+  }
+
+
+  void ThreadedOracle::SetWorkersCount(unsigned int count)
+  {
+    boost::mutex::scoped_lock lock(mutex_);
+
+    if (count <= 0)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+    else if (state_ != State_Setup)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+    else
+    {
+      workers_.resize(count);
+    }
+  }
+
+
+  void ThreadedOracle::SetSleepingTimeResolution(unsigned int milliseconds)
+  {
+    boost::mutex::scoped_lock lock(mutex_);
+
+    if (milliseconds <= 0)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+    else if (state_ != State_Setup)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+    else
+    {
+      sleepingTimeResolution_ = milliseconds;
+    }
+  }
+
+
+  void ThreadedOracle::Start()
+  {
+    boost::mutex::scoped_lock lock(mutex_);
+
+    if (state_ != State_Setup)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+    else
+    {
+      state_ = State_Running;
+
+      for (unsigned int i = 0; i < workers_.size(); i++)
+      {
+        workers_[i] = new boost::thread(Worker, this);
+      }
+
+      sleepingWorker_ = boost::thread(SleepingWorker, this);
+    }      
+  }
+
+
+  void ThreadedOracle::Schedule(const IObserver& receiver,
+                                IOracleCommand* command)
+  {
+    queue_.Enqueue(new Item(receiver, command));
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Oracle/ThreadedOracle.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,96 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#if !defined(ORTHANC_ENABLE_THREADS)
+#  error The macro ORTHANC_ENABLE_THREADS must be defined
+#endif
+
+#if ORTHANC_ENABLE_THREADS != 1
+#  error This file can only compiled for native targets
+#endif
+
+#include "../Messages/IMessageEmitter.h"
+#include "IOracle.h"
+
+#include <Core/WebServiceParameters.h>
+#include <Core/MultiThreading/SharedMessageQueue.h>
+
+
+namespace OrthancStone
+{
+  class ThreadedOracle : public IOracle
+  {
+  private:
+    enum State
+    {
+      State_Setup,
+      State_Running,
+      State_Stopped
+    };
+
+    class Item;
+    class SleepingCommands;
+
+    IMessageEmitter&                     emitter_;
+    Orthanc::WebServiceParameters        orthanc_;
+    Orthanc::SharedMessageQueue          queue_;
+    State                                state_;
+    boost::mutex                         mutex_;
+    std::vector<boost::thread*>          workers_;
+    boost::shared_ptr<SleepingCommands>  sleepingCommands_;
+    boost::thread                        sleepingWorker_;
+    unsigned int                         sleepingTimeResolution_;
+
+    void Step();
+
+    static void Worker(ThreadedOracle* that);
+
+    static void SleepingWorker(ThreadedOracle* that);
+
+    void StopInternal();
+
+  public:
+    ThreadedOracle(IMessageEmitter& emitter);
+
+    virtual ~ThreadedOracle()
+    {
+      StopInternal();
+    }
+
+    void SetOrthancParameters(const Orthanc::WebServiceParameters& orthanc);
+
+    void SetWorkersCount(unsigned int count);
+
+    void SetSleepingTimeResolution(unsigned int milliseconds);
+
+    void Start();
+
+    void Stop()
+    {
+      StopInternal();
+    }
+
+    virtual void Schedule(const IObserver& receiver,
+                          IOracleCommand* command);
+  };
+}
--- a/Framework/Radiography/RadiographyDicomLayer.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Radiography/RadiographyDicomLayer.cpp	Wed May 22 16:13:46 2019 +0200
@@ -22,7 +22,7 @@
 #include "RadiographyDicomLayer.h"
 
 #include "RadiographyScene.h"
-#include "../Toolbox/DicomFrameConverter.h"
+#include "../Deprecated/Toolbox/DicomFrameConverter.h"
 
 #include <Core/OrthancException.h>
 #include <Core/Images/Image.h>
@@ -85,11 +85,11 @@
     {
       if (tmp == "MONOCHROME1")
       {
-        SetPreferredPhotomotricDisplayMode(PhotometricDisplayMode_Monochrome1);
+        SetPreferredPhotomotricDisplayMode(RadiographyPhotometricDisplayMode_Monochrome1);
       }
       else if (tmp == "MONOCHROME2")
       {
-        SetPreferredPhotomotricDisplayMode(PhotometricDisplayMode_Monochrome2);
+        SetPreferredPhotomotricDisplayMode(RadiographyPhotometricDisplayMode_Monochrome2);
       }
     }
   }
--- a/Framework/Radiography/RadiographyDicomLayer.h	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Radiography/RadiographyDicomLayer.h	Wed May 22 16:13:46 2019 +0200
@@ -21,7 +21,7 @@
 
 #pragma once
 
-#include "../Toolbox/DicomFrameConverter.h"
+#include "../Deprecated/Toolbox/DicomFrameConverter.h"
 #include "RadiographyLayer.h"
 
 #include <Plugins/Samples/Common/FullOrthancDataset.h>
--- a/Framework/Radiography/RadiographyLayer.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Radiography/RadiographyLayer.cpp	Wed May 22 16:13:46 2019 +0200
@@ -125,7 +125,7 @@
     hasSize_(false),
     width_(0),
     height_(0),
-    prefferedPhotometricDisplayMode_(PhotometricDisplayMode_Default),
+    prefferedPhotometricDisplayMode_(RadiographyPhotometricDisplayMode_Default),
     scene_(scene)
   {
     UpdateTransform();
@@ -137,7 +137,7 @@
     UpdateTransform();
   }
 
-  void RadiographyLayer::SetPreferredPhotomotricDisplayMode(PhotometricDisplayMode  prefferedPhotometricDisplayMode)
+  void RadiographyLayer::SetPreferredPhotomotricDisplayMode(RadiographyPhotometricDisplayMode  prefferedPhotometricDisplayMode)
   {
     prefferedPhotometricDisplayMode_ = prefferedPhotometricDisplayMode;
 
@@ -349,21 +349,21 @@
     ControlPoint cp;
     switch (index)
     {
-    case ControlPoint_TopLeftCorner:
-      cp = ControlPoint(cropX, cropY, ControlPoint_TopLeftCorner);
-      break;
+      case RadiographyControlPointType_TopLeftCorner:
+        cp = ControlPoint(cropX, cropY, RadiographyControlPointType_TopLeftCorner);
+        break;
 
-    case ControlPoint_TopRightCorner:
-      cp = ControlPoint(cropX + cropWidth, cropY, ControlPoint_TopRightCorner);
-      break;
+      case RadiographyControlPointType_TopRightCorner:
+        cp = ControlPoint(cropX + cropWidth, cropY, RadiographyControlPointType_TopRightCorner);
+        break;
 
-    case ControlPoint_BottomLeftCorner:
-      cp = ControlPoint(cropX, cropY + cropHeight, ControlPoint_BottomLeftCorner);
-      break;
+      case RadiographyControlPointType_BottomLeftCorner:
+        cp = ControlPoint(cropX, cropY + cropHeight, RadiographyControlPointType_BottomLeftCorner);
+        break;
 
-    case ControlPoint_BottomRightCorner:
-      cp = ControlPoint(cropX + cropWidth, cropY + cropHeight, ControlPoint_BottomRightCorner);
-      break;
+      case RadiographyControlPointType_BottomRightCorner:
+        cp = ControlPoint(cropX + cropWidth, cropY + cropHeight, RadiographyControlPointType_BottomRightCorner);
+        break;
 
     default:
       throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
--- a/Framework/Radiography/RadiographyLayer.h	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Radiography/RadiographyLayer.h	Wed May 22 16:13:46 2019 +0200
@@ -31,6 +31,23 @@
 {
   class RadiographyScene;
 
+  enum RadiographyControlPointType
+  {
+    RadiographyControlPointType_TopLeftCorner = 0,
+    RadiographyControlPointType_TopRightCorner = 1,
+    RadiographyControlPointType_BottomRightCorner = 2,
+    RadiographyControlPointType_BottomLeftCorner = 3
+  };
+
+  enum RadiographyPhotometricDisplayMode
+  {
+    RadiographyPhotometricDisplayMode_Default,
+
+    RadiographyPhotometricDisplayMode_Monochrome1,
+    RadiographyPhotometricDisplayMode_Monochrome2
+  };
+
+  
   struct ControlPoint
   {
     double x;
@@ -196,7 +213,7 @@
     AffineTransform2D  transform_;
     AffineTransform2D  transformInverse_;
     Geometry           geometry_;
-    PhotometricDisplayMode  prefferedPhotometricDisplayMode_;
+    RadiographyPhotometricDisplayMode  prefferedPhotometricDisplayMode_;
     const RadiographyScene&   scene_;
 
   protected:
@@ -210,7 +227,7 @@
       return transformInverse_;
     }
 
-    void SetPreferredPhotomotricDisplayMode(PhotometricDisplayMode  prefferedPhotometricDisplayMode);
+    void SetPreferredPhotomotricDisplayMode(RadiographyPhotometricDisplayMode  prefferedPhotometricDisplayMode);
 
   private:
     void UpdateTransform();
@@ -325,7 +342,7 @@
     virtual bool GetDefaultWindowing(float& center,
                                      float& width) const = 0;
 
-    PhotometricDisplayMode GetPreferredPhotomotricDisplayMode() const
+    RadiographyPhotometricDisplayMode GetPreferredPhotomotricDisplayMode() const
     {
       return prefferedPhotometricDisplayMode_;
     }
--- a/Framework/Radiography/RadiographyLayerCropTracker.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Radiography/RadiographyLayerCropTracker.cpp	Wed May 22 16:13:46 2019 +0200
@@ -66,7 +66,7 @@
 
   RadiographyLayerCropTracker::RadiographyLayerCropTracker(UndoRedoStack& undoRedoStack,
                                                            RadiographyScene& scene,
-                                                           const ViewportGeometry& view,
+                                                           const Deprecated::ViewportGeometry& view,
                                                            size_t layer,
                                                            const ControlPoint& startControlPoint) :
     undoRedoStack_(undoRedoStack),
@@ -100,8 +100,8 @@
                                               int displayY,
                                               double sceneX,
                                               double sceneY,
-                                              const std::vector<Touch>& displayTouches,
-                                              const std::vector<Touch>& sceneTouches)
+                                              const std::vector<Deprecated::Touch>& displayTouches,
+                                              const std::vector<Deprecated::Touch>& sceneTouches)
   {
     if (accessor_.IsValid())
     {
@@ -112,8 +112,8 @@
       {
         unsigned int targetX, targetWidth;
 
-        if (startControlPoint_.index == ControlPoint_TopLeftCorner ||
-            startControlPoint_.index == ControlPoint_BottomLeftCorner)
+        if (startControlPoint_.index == RadiographyControlPointType_TopLeftCorner ||
+            startControlPoint_.index == RadiographyControlPointType_BottomLeftCorner)
         {
           targetX = std::min(x, cropX_ + cropWidth_);
           targetWidth = cropX_ + cropWidth_ - targetX;
@@ -126,8 +126,8 @@
 
         unsigned int targetY, targetHeight;
 
-        if (startControlPoint_.index == ControlPoint_TopLeftCorner ||
-            startControlPoint_.index == ControlPoint_TopRightCorner)
+        if (startControlPoint_.index == RadiographyControlPointType_TopLeftCorner ||
+            startControlPoint_.index == RadiographyControlPointType_TopRightCorner)
         {
           targetY = std::min(y, cropY_ + cropHeight_);
           targetHeight = cropY_ + cropHeight_ - targetY;
--- a/Framework/Radiography/RadiographyLayerCropTracker.h	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Radiography/RadiographyLayerCropTracker.h	Wed May 22 16:13:46 2019 +0200
@@ -22,13 +22,13 @@
 #pragma once
 
 #include "../Toolbox/UndoRedoStack.h"
-#include "../Toolbox/ViewportGeometry.h"
-#include "../Widgets/IWorldSceneMouseTracker.h"
+#include "../Deprecated/Toolbox/ViewportGeometry.h"
+#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h"
 #include "RadiographyScene.h"
 
 namespace OrthancStone
 {
-  class RadiographyLayerCropTracker : public IWorldSceneMouseTracker
+  class RadiographyLayerCropTracker : public Deprecated::IWorldSceneMouseTracker
   {
   private:
     class UndoRedoCommand;
@@ -44,7 +44,7 @@
   public:
     RadiographyLayerCropTracker(UndoRedoStack& undoRedoStack,
                                 RadiographyScene& scene,
-                                const ViewportGeometry& view,
+                                const Deprecated::ViewportGeometry& view,
                                 size_t layer,
                                 const ControlPoint& startControlPoint);
 
@@ -62,7 +62,7 @@
                            int displayY,
                            double sceneX,
                            double sceneY,
-                           const std::vector<Touch>& displayTouches,
-                           const std::vector<Touch>& sceneTouches);
+                           const std::vector<Deprecated::Touch>& displayTouches,
+                           const std::vector<Deprecated::Touch>& sceneTouches);
   };
 }
--- a/Framework/Radiography/RadiographyLayerMaskTracker.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Radiography/RadiographyLayerMaskTracker.cpp	Wed May 22 16:13:46 2019 +0200
@@ -85,7 +85,7 @@
 
   RadiographyLayerMaskTracker::RadiographyLayerMaskTracker(UndoRedoStack& undoRedoStack,
                                                            RadiographyScene& scene,
-                                                           const ViewportGeometry& view,
+                                                           const Deprecated::ViewportGeometry& view,
                                                            size_t layer,
                                                            const ControlPoint& startSceneControlPoint) :
     undoRedoStack_(undoRedoStack),
@@ -116,8 +116,8 @@
                                               int displayY,
                                               double sceneX,
                                               double sceneY,
-                                              const std::vector<Touch>& displayTouches,
-                                              const std::vector<Touch>& sceneTouches)
+                                              const std::vector<Deprecated::Touch>& displayTouches,
+                                              const std::vector<Deprecated::Touch>& sceneTouches)
   {
     if (accessor_.IsValid())
     {
--- a/Framework/Radiography/RadiographyLayerMaskTracker.h	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Radiography/RadiographyLayerMaskTracker.h	Wed May 22 16:13:46 2019 +0200
@@ -22,13 +22,13 @@
 #pragma once
 
 #include "../Toolbox/UndoRedoStack.h"
-#include "../Toolbox/ViewportGeometry.h"
-#include "../Widgets/IWorldSceneMouseTracker.h"
+#include "../Deprecated/Toolbox/ViewportGeometry.h"
+#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h"
 #include "RadiographyScene.h"
 
 namespace OrthancStone
 {
-  class RadiographyLayerMaskTracker : public IWorldSceneMouseTracker
+  class RadiographyLayerMaskTracker : public Deprecated::IWorldSceneMouseTracker
   {
   private:
     class UndoRedoCommand;
@@ -41,7 +41,7 @@
   public:
     RadiographyLayerMaskTracker(UndoRedoStack& undoRedoStack,
                                 RadiographyScene& scene,
-                                const ViewportGeometry& view,
+                                const Deprecated::ViewportGeometry& view,
                                 size_t layer,
                                 const ControlPoint& startSceneControlPoint);
 
@@ -59,7 +59,7 @@
                            int displayY,
                            double sceneX,
                            double sceneY,
-                           const std::vector<Touch>& displayTouches,
-                           const std::vector<Touch>& sceneTouches);
+                           const std::vector<Deprecated::Touch>& displayTouches,
+                           const std::vector<Deprecated::Touch>& sceneTouches);
   };
 }
--- a/Framework/Radiography/RadiographyLayerMoveTracker.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Radiography/RadiographyLayerMoveTracker.cpp	Wed May 22 16:13:46 2019 +0200
@@ -98,8 +98,8 @@
                                               int displayY,
                                               double sceneX,
                                               double sceneY,
-                                              const std::vector<Touch>& displayTouches,
-                                              const std::vector<Touch>& sceneTouches)
+                                              const std::vector<Deprecated::Touch>& displayTouches,
+                                              const std::vector<Deprecated::Touch>& sceneTouches)
   {
     if (accessor_.IsValid())
     {
--- a/Framework/Radiography/RadiographyLayerMoveTracker.h	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Radiography/RadiographyLayerMoveTracker.h	Wed May 22 16:13:46 2019 +0200
@@ -22,12 +22,12 @@
 #pragma once
 
 #include "../Toolbox/UndoRedoStack.h"
-#include "../Widgets/IWorldSceneMouseTracker.h"
+#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h"
 #include "RadiographyScene.h"
 
 namespace OrthancStone
 {
-  class RadiographyLayerMoveTracker : public IWorldSceneMouseTracker
+  class RadiographyLayerMoveTracker : public Deprecated::IWorldSceneMouseTracker
   {
   private:
     class UndoRedoCommand;
@@ -62,7 +62,7 @@
                            int displayY,
                            double sceneX,
                            double sceneY,
-                           const std::vector<Touch>& displayTouches,
-                           const std::vector<Touch>& sceneTouches);
+                           const std::vector<Deprecated::Touch>& displayTouches,
+                           const std::vector<Deprecated::Touch>& sceneTouches);
   };
 }
--- a/Framework/Radiography/RadiographyLayerResizeTracker.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Radiography/RadiographyLayerResizeTracker.cpp	Wed May 22 16:13:46 2019 +0200
@@ -102,20 +102,20 @@
       size_t oppositeControlPointType;
       switch (startControlPoint.index)
       {
-        case ControlPoint_TopLeftCorner:
-          oppositeControlPointType = ControlPoint_BottomRightCorner;
+        case RadiographyControlPointType_TopLeftCorner:
+          oppositeControlPointType = RadiographyControlPointType_BottomRightCorner;
           break;
 
-        case ControlPoint_TopRightCorner:
-          oppositeControlPointType = ControlPoint_BottomLeftCorner;
+        case RadiographyControlPointType_TopRightCorner:
+          oppositeControlPointType = RadiographyControlPointType_BottomLeftCorner;
           break;
 
-        case ControlPoint_BottomLeftCorner:
-          oppositeControlPointType = ControlPoint_TopRightCorner;
+        case RadiographyControlPointType_BottomLeftCorner:
+          oppositeControlPointType = RadiographyControlPointType_TopRightCorner;
           break;
 
-        case ControlPoint_BottomRightCorner:
-          oppositeControlPointType = ControlPoint_TopLeftCorner;
+        case RadiographyControlPointType_BottomRightCorner:
+          oppositeControlPointType = RadiographyControlPointType_TopLeftCorner;
           break;
 
         default:
@@ -159,8 +159,8 @@
                                                 int displayY,
                                                 double sceneX,
                                                 double sceneY,
-                                                const std::vector<Touch>& displayTouches,
-                                                const std::vector<Touch>& sceneTouches)
+                                                const std::vector<Deprecated::Touch>& displayTouches,
+                                                const std::vector<Deprecated::Touch>& sceneTouches)
   {
     static const double ROUND_SCALING = 0.1;
         
--- a/Framework/Radiography/RadiographyLayerResizeTracker.h	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Radiography/RadiographyLayerResizeTracker.h	Wed May 22 16:13:46 2019 +0200
@@ -22,12 +22,12 @@
 #pragma once
 
 #include "../Toolbox/UndoRedoStack.h"
-#include "../Widgets/IWorldSceneMouseTracker.h"
+#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h"
 #include "RadiographyScene.h"
 
 namespace OrthancStone
 {
-  class RadiographyLayerResizeTracker : public IWorldSceneMouseTracker
+  class RadiographyLayerResizeTracker : public Deprecated::IWorldSceneMouseTracker
   {
   private:
     class UndoRedoCommand;
@@ -63,7 +63,7 @@
                            int displayY,
                            double sceneX,
                            double sceneY,
-                           const std::vector<Touch>& displayTouches,
-                           const std::vector<Touch>& sceneTouches);
+                           const std::vector<Deprecated::Touch>& displayTouches,
+                           const std::vector<Deprecated::Touch>& sceneTouches);
   };
 }
--- a/Framework/Radiography/RadiographyLayerRotateTracker.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Radiography/RadiographyLayerRotateTracker.cpp	Wed May 22 16:13:46 2019 +0200
@@ -88,7 +88,7 @@
 
   RadiographyLayerRotateTracker::RadiographyLayerRotateTracker(UndoRedoStack& undoRedoStack,
                                                                RadiographyScene& scene,
-                                                               const ViewportGeometry& view,
+                                                               const Deprecated::ViewportGeometry& view,
                                                                size_t layer,
                                                                double x,
                                                                double y,
@@ -133,8 +133,8 @@
                                                 int displayY,
                                                 double sceneX,
                                                 double sceneY,
-                                                const std::vector<Touch>& displayTouches,
-                                                const std::vector<Touch>& sceneTouches)
+                                                const std::vector<Deprecated::Touch>& displayTouches,
+                                                const std::vector<Deprecated::Touch>& sceneTouches)
   {
     static const double ROUND_ANGLE = 15.0 / 180.0 * boost::math::constants::pi<double>(); 
         
--- a/Framework/Radiography/RadiographyLayerRotateTracker.h	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Radiography/RadiographyLayerRotateTracker.h	Wed May 22 16:13:46 2019 +0200
@@ -22,14 +22,14 @@
 #pragma once
 
 #include "../Toolbox/UndoRedoStack.h"
-#include "../Toolbox/ViewportGeometry.h"
-#include "../Widgets/IWorldSceneMouseTracker.h"
+#include "../Deprecated/Toolbox/ViewportGeometry.h"
+#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h"
 #include "RadiographyScene.h"
 
 
 namespace OrthancStone
 {
-  class RadiographyLayerRotateTracker : public IWorldSceneMouseTracker
+  class RadiographyLayerRotateTracker : public Deprecated::IWorldSceneMouseTracker
   {
   private:
     class UndoRedoCommand;
@@ -49,7 +49,7 @@
   public:
     RadiographyLayerRotateTracker(UndoRedoStack& undoRedoStack,
                                   RadiographyScene& scene,
-                                  const ViewportGeometry& view,
+                                  const Deprecated::ViewportGeometry& view,
                                   size_t layer,
                                   double x,
                                   double y,
@@ -69,7 +69,7 @@
                            int displayY,
                            double sceneX,
                            double sceneY,
-                           const std::vector<Touch>& displayTouches,
-                           const std::vector<Touch>& sceneTouches);
+                           const std::vector<Deprecated::Touch>& displayTouches,
+                           const std::vector<Deprecated::Touch>& sceneTouches);
   };
 }
--- a/Framework/Radiography/RadiographyMaskLayer.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Radiography/RadiographyMaskLayer.cpp	Wed May 22 16:13:46 2019 +0200
@@ -135,8 +135,16 @@
     // first fill the complete image
     Orthanc::ImageProcessing::Set(*mask_, OUT_MASK_VALUE);
 
+    // clip corners
+    std::vector<Orthanc::ImageProcessing::ImagePoint> clippedCorners;
+    for (size_t i = 0; i < corners_.size(); i++)
+    {
+      clippedCorners.push_back(corners_[i]);
+      clippedCorners[i].ClipTo(0, mask_->GetWidth() - 1, 0, mask_->GetHeight() - 1);
+    }
+
     // fill mask
-    Orthanc::ImageProcessing::FillPolygon(*mask_, corners_, IN_MASK_VALUE);
+    Orthanc::ImageProcessing::FillPolygon(*mask_, clippedCorners, IN_MASK_VALUE);
 
   }
 
--- a/Framework/Radiography/RadiographyScene.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Radiography/RadiographyScene.cpp	Wed May 22 16:13:46 2019 +0200
@@ -25,7 +25,7 @@
 #include "RadiographyDicomLayer.h"
 #include "RadiographyTextLayer.h"
 #include "RadiographyMaskLayer.h"
-#include "../Toolbox/DicomFrameConverter.h"
+#include "../Deprecated/Toolbox/DicomFrameConverter.h"
 
 #include <Core/Images/Image.h>
 #include <Core/Images/ImageProcessing.h>
@@ -172,18 +172,18 @@
     }
   }
 
-  PhotometricDisplayMode RadiographyScene::GetPreferredPhotomotricDisplayMode() const
+  RadiographyPhotometricDisplayMode RadiographyScene::GetPreferredPhotomotricDisplayMode() const
   {
     // return the mode of the first layer who "cares" about its display mode (normaly, the one and only layer that is a DicomLayer)
     for (Layers::const_iterator it = layers_.begin(); it != layers_.end(); it++)
     {
-      if (it->second->GetPreferredPhotomotricDisplayMode() != PhotometricDisplayMode_Default)
+      if (it->second->GetPreferredPhotomotricDisplayMode() != RadiographyPhotometricDisplayMode_Default)
       {
         return it->second->GetPreferredPhotomotricDisplayMode();
       }
     }
 
-    return PhotometricDisplayMode_Default;
+    return RadiographyPhotometricDisplayMode_Default;
   }
 
 
@@ -345,7 +345,7 @@
                                                      const std::string& instance,
                                                      unsigned int frame,
                                                      Deprecated::DicomFrameConverter* converter,  // takes ownership
-                                                     PhotometricDisplayMode preferredPhotometricDisplayMode,
+                                                     RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode,
                                                      RadiographyLayer::Geometry* geometry)
   {
     RadiographyDicomLayer& layer = dynamic_cast<RadiographyDicomLayer&>(RegisterLayer(new RadiographyDicomLayer(IObservable::GetBroker(), *this)));
@@ -364,7 +364,7 @@
     return layer;
   }
 
-  RadiographyLayer& RadiographyScene::LoadDicomFrame(OrthancApiClient& orthanc,
+  RadiographyLayer& RadiographyScene::LoadDicomFrame(Deprecated::OrthancApiClient& orthanc,
                                                      const std::string& instance,
                                                      unsigned int frame,
                                                      bool httpCompression,
@@ -379,18 +379,18 @@
     }
 
     {
-      IWebService::HttpHeaders headers;
+      Deprecated::IWebService::HttpHeaders headers;
       std::string uri = "/instances/" + instance + "/tags";
 
       orthanc.GetBinaryAsync(
             uri, headers,
-            new Callable<RadiographyScene, OrthancApiClient::BinaryResponseReadyMessage>
+            new Callable<RadiographyScene, Deprecated::OrthancApiClient::BinaryResponseReadyMessage>
             (*this, &RadiographyScene::OnTagsReceived), NULL,
             new Orthanc::SingleValueObject<size_t>(layer.GetIndex()));
     }
 
     {
-      IWebService::HttpHeaders headers;
+      Deprecated::IWebService::HttpHeaders headers;
       headers["Accept"] = "image/x-portable-arbitrarymap";
 
       if (httpCompression)
@@ -403,7 +403,7 @@
 
       orthanc.GetBinaryAsync(
             uri, headers,
-            new Callable<RadiographyScene, OrthancApiClient::BinaryResponseReadyMessage>
+            new Callable<RadiographyScene, Deprecated::OrthancApiClient::BinaryResponseReadyMessage>
             (*this, &RadiographyScene::OnFrameReceived), NULL,
             new Orthanc::SingleValueObject<size_t>(layer.GetIndex()));
     }
@@ -412,7 +412,7 @@
   }
 
 
-  RadiographyLayer& RadiographyScene::LoadDicomWebFrame(IWebService& web)
+  RadiographyLayer& RadiographyScene::LoadDicomWebFrame(Deprecated::IWebService& web)
   {
     RadiographyLayer& layer = RegisterLayer(new RadiographyDicomLayer(IObservable::GetBroker(), *this));
 
@@ -422,7 +422,7 @@
 
 
 
-  void RadiographyScene::OnTagsReceived(const OrthancApiClient::BinaryResponseReadyMessage& message)
+  void RadiographyScene::OnTagsReceived(const Deprecated::OrthancApiClient::BinaryResponseReadyMessage& message)
   {
     size_t index = dynamic_cast<const Orthanc::SingleValueObject<size_t>&>
         (message.GetPayload()).GetValue();
@@ -452,7 +452,7 @@
   }
 
 
-  void RadiographyScene::OnFrameReceived(const OrthancApiClient::BinaryResponseReadyMessage& message)
+  void RadiographyScene::OnFrameReceived(const Deprecated::OrthancApiClient::BinaryResponseReadyMessage& message)
   {
     size_t index = dynamic_cast<const Orthanc::SingleValueObject<size_t>&>(message.GetPayload()).GetValue();
 
@@ -642,9 +642,9 @@
 
     createDicomRequestContent["Tags"] = dicomTags;
 
-    PhotometricDisplayMode photometricMode = GetPreferredPhotomotricDisplayMode();
-    if ((invert && photometricMode != PhotometricDisplayMode_Monochrome2) ||
-        (!invert && photometricMode == PhotometricDisplayMode_Monochrome1))
+    RadiographyPhotometricDisplayMode photometricMode = GetPreferredPhotomotricDisplayMode();
+    if ((invert && photometricMode != RadiographyPhotometricDisplayMode_Monochrome2) ||
+        (!invert && photometricMode == RadiographyPhotometricDisplayMode_Monochrome1))
     {
       createDicomRequestContent["Tags"]["PhotometricInterpretation"] = "MONOCHROME1";
     }
@@ -726,7 +726,7 @@
   }
 
 
-  void RadiographyScene::ExportDicom(OrthancApiClient& orthanc,
+  void RadiographyScene::ExportDicom(Deprecated::OrthancApiClient& orthanc,
                                      const Json::Value& dicomTags,
                                      const std::string& parentOrthancId,
                                      double pixelSpacingX,
@@ -741,7 +741,7 @@
 
     orthanc.PostJsonAsyncExpectJson(
           "/tools/create-dicom", createDicomRequestContent,
-          new Callable<RadiographyScene, OrthancApiClient::JsonResponseReadyMessage>
+          new Callable<RadiographyScene, Deprecated::OrthancApiClient::JsonResponseReadyMessage>
           (*this, &RadiographyScene::OnDicomExported),
           NULL, NULL);
 
@@ -750,7 +750,7 @@
 
   // Export using PAM is faster than using PNG, but requires Orthanc
   // core >= 1.4.3
-  void RadiographyScene::ExportDicom(OrthancApiClient& orthanc,
+  void RadiographyScene::ExportDicom(Deprecated::OrthancApiClient& orthanc,
                                      const Orthanc::DicomMap& dicom,
                                      const std::string& parentOrthancId,
                                      double pixelSpacingX,
@@ -778,19 +778,19 @@
     ExportDicom(orthanc, jsonTags, parentOrthancId, pixelSpacingX, pixelSpacingY, invert, interpolation, usePam);
   }
 
-  void RadiographyScene::OnDicomExported(const OrthancApiClient::JsonResponseReadyMessage& message)
+  void RadiographyScene::OnDicomExported(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message)
   {
     LOG(INFO) << "DICOM export was successful: "
               << message.GetJson().toStyledString();
   }
 
 
-  void RadiographyScene::OnDicomWebReceived(const IWebService::HttpRequestSuccessMessage& message)
+  void RadiographyScene::OnDicomWebReceived(const Deprecated::IWebService::HttpRequestSuccessMessage& message)
   {
     LOG(INFO) << "DICOMweb WADO-RS received: " << message.GetAnswerSize() << " bytes";
 
-    const IWebService::HttpHeaders& h = message.GetAnswerHttpHeaders();
-    for (IWebService::HttpHeaders::const_iterator
+    const Deprecated::IWebService::HttpHeaders& h = message.GetAnswerHttpHeaders();
+    for (Deprecated::IWebService::HttpHeaders::const_iterator
          it = h.begin(); it != h.end(); ++it)
     {
       printf("[%s] = [%s]\n", it->first.c_str(), it->second.c_str());
--- a/Framework/Radiography/RadiographyScene.h	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Radiography/RadiographyScene.h	Wed May 22 16:13:46 2019 +0200
@@ -22,9 +22,9 @@
 #pragma once
 
 #include "RadiographyLayer.h"
-#include "../Toolbox/DicomFrameConverter.h"
-#include "../Toolbox/OrthancApiClient.h"
-#include "Framework/StoneEnumerations.h"
+#include "../Deprecated/Toolbox/DicomFrameConverter.h"
+#include "../Deprecated/Toolbox/OrthancApiClient.h"
+#include "../StoneEnumerations.h"
 #include "Core/Images/Image.h"
 #include "Core/Images/ImageProcessing.h"
 
@@ -149,15 +149,15 @@
   protected:
     RadiographyLayer& RegisterLayer(RadiographyLayer* layer);
 
-    void OnTagsReceived(const OrthancApiClient::BinaryResponseReadyMessage& message);
+    void OnTagsReceived(const Deprecated::OrthancApiClient::BinaryResponseReadyMessage& message);
 
-    virtual void OnFrameReceived(const OrthancApiClient::BinaryResponseReadyMessage& message);
+    virtual void OnFrameReceived(const Deprecated::OrthancApiClient::BinaryResponseReadyMessage& message);
     
-    void OnDicomExported(const OrthancApiClient::JsonResponseReadyMessage& message);
+    void OnDicomExported(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message);
 
-    void OnDicomWebReceived(const IWebService::HttpRequestSuccessMessage& message);
+    void OnDicomWebReceived(const Deprecated::IWebService::HttpRequestSuccessMessage& message);
 
-    void OnLayerEdited(const RadiographyLayer::LayerEditedMessage& message);
+    virtual void OnLayerEdited(const RadiographyLayer::LayerEditedMessage& message);
   public:
     RadiographyScene(MessageBroker& broker);
     
@@ -169,10 +169,10 @@
     void GetWindowingWithDefault(float& center,
                                  float& width) const;
 
-    void SetWindowing(float center,
-                      float width);
+    virtual void SetWindowing(float center,
+                              float width);
 
-    PhotometricDisplayMode GetPreferredPhotomotricDisplayMode() const;
+    RadiographyPhotometricDisplayMode GetPreferredPhotomotricDisplayMode() const;
 
     RadiographyLayer& LoadText(const Orthanc::Font& font,
                                const std::string& utf8,
@@ -194,16 +194,16 @@
                                              const std::string& instance,
                                              unsigned int frame,
                                              Deprecated::DicomFrameConverter* converter,  // takes ownership
-                                             PhotometricDisplayMode preferredPhotometricDisplayMode,
+                                             RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode,
                                              RadiographyLayer::Geometry* geometry);
 
-    virtual RadiographyLayer& LoadDicomFrame(OrthancApiClient& orthanc,
+    virtual RadiographyLayer& LoadDicomFrame(Deprecated::OrthancApiClient& orthanc,
                                              const std::string& instance,
                                              unsigned int frame,
                                              bool httpCompression,
                                              RadiographyLayer::Geometry* geometry); // pass NULL if you want default geometry
 
-    RadiographyLayer& LoadDicomWebFrame(IWebService& web);
+    RadiographyLayer& LoadDicomWebFrame(Deprecated::IWebService& web);
 
     void RemoveLayer(size_t layerIndex);
 
@@ -278,7 +278,7 @@
 
     // Export using PAM is faster than using PNG, but requires Orthanc
     // core >= 1.4.3
-    void ExportDicom(OrthancApiClient& orthanc,
+    void ExportDicom(Deprecated::OrthancApiClient& orthanc,
                      const Orthanc::DicomMap& dicom,
                      const std::string& parentOrthancId,
                      double pixelSpacingX,
@@ -287,7 +287,7 @@
                      ImageInterpolation interpolation,
                      bool usePam);
 
-    void ExportDicom(OrthancApiClient& orthanc,
+    void ExportDicom(Deprecated::OrthancApiClient& orthanc,
                      const Json::Value& dicomTags,
                      const std::string& parentOrthancId,
                      double pixelSpacingX,
--- a/Framework/Radiography/RadiographySceneReader.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Radiography/RadiographySceneReader.cpp	Wed May 22 16:13:46 2019 +0200
@@ -21,7 +21,7 @@
 
 #include "RadiographySceneReader.h"
 
-#include <Framework/Toolbox/DicomFrameConverter.h>
+#include "../Deprecated/Toolbox/DicomFrameConverter.h"
 
 #include <Core/Images/FontRegistry.h>
 #include <Core/Images/PngReader.h>
@@ -33,7 +33,7 @@
 
   void RadiographySceneBuilder::Read(const Json::Value& input, Orthanc::ImageAccessor* dicomImage /* takes ownership */,
                                      Deprecated::DicomFrameConverter* dicomFrameConverter  /* takes ownership */,
-                                     PhotometricDisplayMode preferredPhotometricDisplayMode
+                                     RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode
                                      )
   {
     dicomImage_.reset(dicomImage);
--- a/Framework/Radiography/RadiographySceneReader.h	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Radiography/RadiographySceneReader.h	Wed May 22 16:13:46 2019 +0200
@@ -26,13 +26,13 @@
 #include "RadiographyDicomLayer.h"
 #include "RadiographyMaskLayer.h"
 #include "RadiographyTextLayer.h"
+#include "../Deprecated/Toolbox/OrthancApiClient.h"
+
 #include <json/value.h>
 #include <Core/Images/FontRegistry.h>
 
 namespace OrthancStone
 {
-  class OrthancApiClient;
-
   // HACK: I had to introduce this builder class in order to be able to recreate a RadiographyScene
   // from a serialized scene that is passed to web-workers.
   // It needs some architecturing...
@@ -42,8 +42,8 @@
     RadiographyScene&                               scene_;
     const Orthanc::FontRegistry*                    fontRegistry_;
     std::auto_ptr<Orthanc::ImageAccessor>           dicomImage_;
-    std::auto_ptr<Deprecated::DicomFrameConverter>              dicomFrameConverter_;
-    PhotometricDisplayMode                          preferredPhotometricDisplayMode_;
+    std::auto_ptr<Deprecated::DicomFrameConverter>  dicomFrameConverter_;
+    RadiographyPhotometricDisplayMode               preferredPhotometricDisplayMode_;
 
   public:
     RadiographySceneBuilder(RadiographyScene& scene) :
@@ -56,7 +56,7 @@
     void Read(const Json::Value& input,
               Orthanc::ImageAccessor* dicomImage, // takes ownership
               Deprecated::DicomFrameConverter* dicomFrameConverter, // takes ownership
-              PhotometricDisplayMode preferredPhotometricDisplayMode
+              RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode
               );
 
     void SetFontRegistry(const Orthanc::FontRegistry& fontRegistry)
@@ -72,10 +72,10 @@
 
   class RadiographySceneReader : public RadiographySceneBuilder
   {
-    OrthancApiClient&             orthancApiClient_;
+    Deprecated::OrthancApiClient&             orthancApiClient_;
 
   public:
-    RadiographySceneReader(RadiographyScene& scene, OrthancApiClient& orthancApiClient) :
+    RadiographySceneReader(RadiographyScene& scene, Deprecated::OrthancApiClient& orthancApiClient) :
       RadiographySceneBuilder(scene),
       orthancApiClient_(orthancApiClient)
     {
--- a/Framework/Radiography/RadiographyWidget.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Radiography/RadiographyWidget.cpp	Wed May 22 16:13:46 2019 +0200
@@ -35,7 +35,7 @@
     // MONOCHROME1 images must be inverted and the user can invert the 
     // image, too -> XOR the two
     return (scene_->GetPreferredPhotomotricDisplayMode() == 
-      PhotometricDisplayMode_Monochrome1) ^ invert_; 
+            RadiographyPhotometricDisplayMode_Monochrome1) ^ invert_; 
   }
 
   void RadiographyWidget::RenderBackground(
@@ -46,14 +46,14 @@
 
     switch (scene_->GetPreferredPhotomotricDisplayMode())
     {
-    case PhotometricDisplayMode_Monochrome1:
-    case PhotometricDisplayMode_Default:
+    case RadiographyPhotometricDisplayMode_Monochrome1:
+    case RadiographyPhotometricDisplayMode_Default:
       if (IsInvertedInternal())
         backgroundValue = maxValue;
       else
         backgroundValue = minValue;
       break;
-    case PhotometricDisplayMode_Monochrome2:
+    case RadiographyPhotometricDisplayMode_Monochrome2:
       if (IsInvertedInternal())
         backgroundValue = minValue;
       else
@@ -151,7 +151,7 @@
 
 
   bool RadiographyWidget::RenderScene(CairoContext& context,
-                                      const ViewportGeometry& view)
+                                      const Deprecated::ViewportGeometry& view)
   {
     cairo_t* cr = context.GetObject();
 
--- a/Framework/Radiography/RadiographyWidget.h	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Radiography/RadiographyWidget.h	Wed May 22 16:13:46 2019 +0200
@@ -21,7 +21,7 @@
 
 #pragma once
 
-#include "../Widgets/WorldSceneWidget.h"
+#include "../Deprecated/Widgets/WorldSceneWidget.h"
 #include "RadiographyScene.h"
 
 
@@ -30,7 +30,7 @@
   class RadiographyMaskLayer;
 
   class RadiographyWidget :
-    public WorldSceneWidget,
+    public Deprecated::WorldSceneWidget,
     public IObserver
   {
   private:
@@ -53,7 +53,7 @@
     }
 
     virtual bool RenderScene(CairoContext& context,
-                             const ViewportGeometry& view);
+                             const Deprecated::ViewportGeometry& view);
 
     virtual void RenderBackground(Orthanc::ImageAccessor& image, float minValue, float maxValue);
 
--- a/Framework/Radiography/RadiographyWindowingTracker.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Radiography/RadiographyWindowingTracker.cpp	Wed May 22 16:13:46 2019 +0200
@@ -164,8 +164,8 @@
                                               int displayY,
                                               double sceneX,
                                               double sceneY,
-                                              const std::vector<Touch>& displayTouches,
-                                              const std::vector<Touch>& sceneTouches)
+                                              const std::vector<Deprecated::Touch>& displayTouches,
+                                              const std::vector<Deprecated::Touch>& sceneTouches)
   {
     // This follows the behavior of the Osimis Web viewer:
     // https://bitbucket.org/osimis/osimis-webviewer-plugin/src/master/frontend/src/app/viewport/image-plugins/windowing-viewport-tool.class.js
--- a/Framework/Radiography/RadiographyWindowingTracker.h	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Radiography/RadiographyWindowingTracker.h	Wed May 22 16:13:46 2019 +0200
@@ -22,12 +22,12 @@
 #pragma once
 
 #include "../Toolbox/UndoRedoStack.h"
-#include "../Widgets/IWorldSceneMouseTracker.h"
+#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h"
 #include "RadiographyScene.h"
 
 namespace OrthancStone
 {
-  class RadiographyWindowingTracker : public IWorldSceneMouseTracker
+  class RadiographyWindowingTracker : public Deprecated::IWorldSceneMouseTracker
   {   
   public:
     enum Action
@@ -83,7 +83,7 @@
                            int displayY,
                            double sceneX,
                            double sceneY,
-                           const std::vector<Touch>& displayTouches,
-                           const std::vector<Touch>& sceneTouches);
+                           const std::vector<Deprecated::Touch>& displayTouches,
+                           const std::vector<Deprecated::Touch>& sceneTouches);
   };
 }
--- a/Framework/Scene2D/Internals/FixedPointAligner.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Scene2D/Internals/FixedPointAligner.cpp	Wed May 22 16:13:46 2019 +0200
@@ -18,7 +18,7 @@
  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  **/
 
-#include <Framework/Scene2DViewport/ViewportController.h>
+#include "../../Scene2DViewport/ViewportController.h"
 #include "FixedPointAligner.h"
 
 namespace OrthancStone
--- a/Framework/Scene2D/Internals/FixedPointAligner.h	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Scene2D/Internals/FixedPointAligner.h	Wed May 22 16:13:46 2019 +0200
@@ -20,15 +20,15 @@
 
 #pragma once
 
-#include <Framework/Scene2DViewport/PointerTypes.h>
-#include <Framework/Scene2D/ScenePoint2D.h>
+#include "../../Scene2DViewport/PointerTypes.h"
+#include "../../Scene2D/ScenePoint2D.h"
 
 namespace OrthancStone
 {
   namespace Internals
   {
     // During a mouse event that modifies the view of a scene, keeps
-    // one point (the pivot) at the same position on the canvas
+    // one point (the pivot) at a fixed position on the canvas
     class FixedPointAligner : public boost::noncopyable
     {
     private:
--- a/Framework/Scene2D/PanSceneTracker.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Scene2D/PanSceneTracker.cpp	Wed May 22 16:13:46 2019 +0200
@@ -20,7 +20,7 @@
 
 
 #include "PanSceneTracker.h"
-#include <Framework/Scene2DViewport/ViewportController.h>
+#include "../Scene2DViewport/ViewportController.h"
 
 namespace OrthancStone
 {
--- a/Framework/Scene2D/PanSceneTracker.h	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Scene2D/PanSceneTracker.h	Wed May 22 16:13:46 2019 +0200
@@ -25,8 +25,6 @@
 
 namespace OrthancStone
 {
-  class ViewportController;
-
   class PanSceneTracker : public OneGesturePointerTracker
   {
   public:
--- a/Framework/Scene2D/RotateSceneTracker.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Scene2D/RotateSceneTracker.cpp	Wed May 22 16:13:46 2019 +0200
@@ -19,7 +19,7 @@
  **/
 
 #include "RotateSceneTracker.h"
-#include <Framework/Scene2DViewport/ViewportController.h>
+#include "../Scene2DViewport/ViewportController.h"
 
 namespace OrthancStone
 {
--- a/Framework/Scene2D/RotateSceneTracker.h	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Scene2D/RotateSceneTracker.h	Wed May 22 16:13:46 2019 +0200
@@ -26,8 +26,6 @@
 
 namespace OrthancStone
 {
-  class ViewportController;
-
   class RotateSceneTracker : public OneGesturePointerTracker
   {
   public:
--- a/Framework/Scene2D/Scene2D.h	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Scene2D/Scene2D.h	Wed May 22 16:13:46 2019 +0200
@@ -23,8 +23,8 @@
 
 #include "ISceneLayer.h"
 #include "../Toolbox/AffineTransform2D.h"
-#include <Framework/Messages/IObservable.h>
-#include <Framework/Messages/IMessage.h>
+#include "../Messages/IObservable.h"
+#include "../Messages/IMessage.h"
 
 #include <map>
 
--- a/Framework/Scene2D/ZoomSceneTracker.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Scene2D/ZoomSceneTracker.cpp	Wed May 22 16:13:46 2019 +0200
@@ -20,7 +20,7 @@
 
 
 #include "ZoomSceneTracker.h"
-#include <Framework/Scene2DViewport/ViewportController.h>
+#include "../Scene2DViewport/ViewportController.h"
 
 namespace OrthancStone
 {
--- a/Framework/Scene2D/ZoomSceneTracker.h	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Scene2D/ZoomSceneTracker.h	Wed May 22 16:13:46 2019 +0200
@@ -27,8 +27,6 @@
 
 namespace OrthancStone
 {
-  class Scene2D;
-
   class ZoomSceneTracker : public OneGesturePointerTracker
   {
   public:
--- a/Framework/Scene2DViewport/AngleMeasureTool.h	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Scene2DViewport/AngleMeasureTool.h	Wed May 22 16:13:46 2019 +0200
@@ -22,13 +22,12 @@
 
 #include "MeasureTools.h"
 
+#include "../Scene2DViewport/LayerHolder.h"
 #include "../Scene2D/Scene2D.h"
 #include "../Scene2D/ScenePoint2D.h"
 #include "../Scene2D/PolylineSceneLayer.h"
 #include "../Scene2D/TextSceneLayer.h"
 
-#include "../Scene2DViewport/LayerHolder.h"
-
 #include <boost/shared_ptr.hpp>
 #include <boost/weak_ptr.hpp>
 
--- a/Framework/Scene2DViewport/CreateAngleMeasureTracker.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Scene2DViewport/CreateAngleMeasureTracker.cpp	Wed May 22 16:13:46 2019 +0200
@@ -21,8 +21,6 @@
 #include "CreateAngleMeasureTracker.h"
 #include <Core/OrthancException.h>
 
-using namespace Orthanc;
-
 namespace OrthancStone
 {
   CreateAngleMeasureTracker::CreateAngleMeasureTracker(
@@ -49,7 +47,7 @@
 
     if (!alive_)
     {
-      throw OrthancException(ErrorCode_InternalError,
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
         "Internal error: wrong state in CreateAngleMeasureTracker::"
         "PointerMove: active_ == false");
     }
@@ -66,7 +64,7 @@
       GetCommand()->SetSide2End(scenePos);
       break;
     default:
-      throw OrthancException(ErrorCode_InternalError,
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
         "Wrong state in CreateAngleMeasureTracker::"
         "PointerMove: state_ invalid");
     }
@@ -88,12 +86,12 @@
       state_ = CreatingSide2;
       break;
     case CreatingSide2:
-      throw OrthancException(ErrorCode_InternalError,
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
         "Wrong state in CreateAngleMeasureTracker::"
         "PointerUp: state_ == CreatingSide2 ; this should not happen");
       break;
     default:
-      throw OrthancException(ErrorCode_InternalError,
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
         "Wrong state in CreateAngleMeasureTracker::"
         "PointerMove: state_ invalid");
     }
@@ -104,7 +102,7 @@
     switch (state_)
     {
     case CreatingSide1:
-      throw OrthancException(ErrorCode_InternalError,
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
         "Wrong state in CreateAngleMeasureTracker::"
         "PointerDown: state_ == CreatingSide1 ; this should not happen");
       break;
@@ -113,7 +111,7 @@
       alive_ = false;
       break;
     default:
-      throw OrthancException(ErrorCode_InternalError,
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
         "Wrong state in CreateAngleMeasureTracker::"
         "PointerMove: state_ invalid");
     }
--- a/Framework/Scene2DViewport/CreateLineMeasureTracker.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Scene2DViewport/CreateLineMeasureTracker.cpp	Wed May 22 16:13:46 2019 +0200
@@ -21,8 +21,6 @@
 #include "CreateLineMeasureTracker.h"
 #include <Core/OrthancException.h>
 
-using namespace Orthanc;
-
 namespace OrthancStone
 {
   CreateLineMeasureTracker::CreateLineMeasureTracker(
@@ -49,7 +47,7 @@
     
     if (!alive_)
     {
-      throw OrthancException(ErrorCode_InternalError,
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,
         "Internal error: wrong state in CreateLineMeasureTracker::"
         "PointerMove: active_ == false");
     }
--- a/Framework/Scene2DViewport/CreateSimpleTrackerAdapter.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Scene2DViewport/CreateSimpleTrackerAdapter.cpp	Wed May 22 16:13:46 2019 +0200
@@ -19,7 +19,7 @@
  **/
 
 #include "IFlexiblePointerTracker.h"
-#include <Framework/Scene2D/IPointerTracker.h>
+#include "../Scene2D/IPointerTracker.h"
 
 
 namespace OrthancStone
--- a/Framework/Scene2DViewport/IFlexiblePointerTracker.h	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Scene2DViewport/IFlexiblePointerTracker.h	Wed May 22 16:13:46 2019 +0200
@@ -23,7 +23,7 @@
 
 #include "PointerTypes.h"
 
-#include <Framework/Scene2D/PointerEvent.h>
+#include "../Scene2D/PointerEvent.h"
 
 namespace OrthancStone
 {
--- a/Framework/Scene2DViewport/LineMeasureTool.h	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Scene2DViewport/LineMeasureTool.h	Wed May 22 16:13:46 2019 +0200
@@ -20,13 +20,12 @@
 
 #pragma once
 
+#include "../Scene2D/PolylineSceneLayer.h"
+#include "../Scene2D/Scene2D.h"
+#include "../Scene2D/ScenePoint2D.h"
+#include "../Scene2D/TextSceneLayer.h"
 #include "MeasureTools.h"
 
-#include <Framework/Scene2D/Scene2D.h>
-#include <Framework/Scene2D/ScenePoint2D.h>
-#include <Framework/Scene2D/PolylineSceneLayer.h>
-#include <Framework/Scene2D/TextSceneLayer.h>
-
 #include <boost/shared_ptr.hpp>
 #include <boost/weak_ptr.hpp>
 
--- a/Framework/Scene2DViewport/MeasureCommands.h	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Scene2DViewport/MeasureCommands.h	Wed May 22 16:13:46 2019 +0200
@@ -19,9 +19,7 @@
  **/
 #pragma once
 
-#include <Framework/Scene2D/Scene2D.h>
-#include <boost/shared_ptr.hpp>
-#include <boost/noncopyable.hpp>
+#include "../Scene2D/Scene2D.h"
 
 // to be moved into Stone
 #include "PointerTypes.h"
@@ -29,6 +27,9 @@
 #include "LineMeasureTool.h"
 #include "AngleMeasureTool.h"
 
+#include <boost/shared_ptr.hpp>
+#include <boost/noncopyable.hpp>
+
 namespace OrthancStone
 {
   class TrackerCommand : public boost::noncopyable
--- a/Framework/Scene2DViewport/MeasureTools.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Scene2DViewport/MeasureTools.cpp	Wed May 22 16:13:46 2019 +0200
@@ -26,8 +26,6 @@
 
 #include <boost/math/constants/constants.hpp>
 
-using namespace Orthanc;
-
 namespace OrthancStone
 {
 
@@ -58,7 +56,7 @@
   {
     ViewportControllerPtr controller = controllerW_.lock();
     if (!controller)
-      throw OrthancException(ErrorCode_InternalError, 
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, 
         "Using dead ViewportController object!");
     return controller;
   }
--- a/Framework/Scene2DViewport/MeasureTools.h	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Scene2DViewport/MeasureTools.h	Wed May 22 16:13:46 2019 +0200
@@ -20,13 +20,12 @@
 
 #pragma once
 
-#include <Framework/Scene2DViewport/PointerTypes.h>
-#include <Framework/Scene2DViewport/ViewportController.h>
-
-#include <Framework/Scene2D/Scene2D.h>
-#include <Framework/Scene2D/ScenePoint2D.h>
-#include <Framework/Scene2D/PolylineSceneLayer.h>
-#include <Framework/Scene2D/TextSceneLayer.h>
+#include "../Scene2D/PolylineSceneLayer.h"
+#include "../Scene2D/Scene2D.h"
+#include "../Scene2D/ScenePoint2D.h"
+#include "../Scene2D/TextSceneLayer.h"
+#include "../Scene2DViewport/PointerTypes.h"
+#include "../Scene2DViewport/ViewportController.h"
 
 #include <boost/shared_ptr.hpp>
 #include <boost/weak_ptr.hpp>
--- a/Framework/Scene2DViewport/MeasureToolsToolbox.h	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Scene2DViewport/MeasureToolsToolbox.h	Wed May 22 16:13:46 2019 +0200
@@ -18,8 +18,9 @@
  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  **/
 
+#include "PointerTypes.h"
 #include "../Scene2D/PolylineSceneLayer.h"
-#include "PointerTypes.h"
+#include "../Scene2D/Scene2D.h"
 
 namespace OrthancStone
 {
--- a/Framework/Scene2DViewport/MeasureTrackers.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Scene2DViewport/MeasureTrackers.cpp	Wed May 22 16:13:46 2019 +0200
@@ -21,8 +21,6 @@
 #include "MeasureTrackers.h"
 #include <Core/OrthancException.h>
 
-using namespace Orthanc;
-
 namespace OrthancStone
 {
 
--- a/Framework/Scene2DViewport/MeasureTrackers.h	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Scene2DViewport/MeasureTrackers.h	Wed May 22 16:13:46 2019 +0200
@@ -21,8 +21,8 @@
 #pragma once
 
 #include "IFlexiblePointerTracker.h"
-#include "../../Framework/Scene2D/Scene2D.h"
-#include "../../Framework/Scene2D/PointerEvent.h"
+#include "../Scene2D/Scene2D.h"
+#include "../Scene2D/PointerEvent.h"
 
 #include "MeasureTools.h"
 #include "MeasureCommands.h"
--- a/Framework/Scene2DViewport/OneGesturePointerTracker.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Scene2DViewport/OneGesturePointerTracker.cpp	Wed May 22 16:13:46 2019 +0200
@@ -23,9 +23,7 @@
 
 #include <Core/OrthancException.h>
 
-#include <Framework/StoneException.h>
-
-using namespace Orthanc;
+#include "../StoneException.h"
 
 namespace OrthancStone
 {
--- a/Framework/Scene2DViewport/ViewportController.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Scene2DViewport/ViewportController.cpp	Wed May 22 16:13:46 2019 +0200
@@ -21,12 +21,10 @@
 #include "ViewportController.h"
 #include "MeasureCommands.h"
 
-#include <Framework/StoneException.h>
+#include "../StoneException.h"
 
 #include <boost/make_shared.hpp>
 
-using namespace Orthanc;
-
 namespace OrthancStone
 {
   ViewportController::ViewportController(MessageBroker& broker)
--- a/Framework/Scene2DViewport/ViewportController.h	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Scene2DViewport/ViewportController.h	Wed May 22 16:13:46 2019 +0200
@@ -22,9 +22,9 @@
 
 #include "PointerTypes.h"
 
-#include <Framework/Scene2D/Scene2D.h>
-#include <Framework/Scene2D/PointerEvent.h>
-#include <Framework/Scene2DViewport/IFlexiblePointerTracker.h>
+#include "../Scene2D/Scene2D.h"
+#include "../Scene2D/PointerEvent.h"
+#include "../Scene2DViewport/IFlexiblePointerTracker.h"
 
 #include <stack>
 
--- a/Framework/SmartLoader.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,291 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "SmartLoader.h"
-#include "Layers/DicomSeriesVolumeSlicer.h"
-#include "Messages/MessageForwarder.h"
-#include "Core/Images/Image.h"
-#include "Framework/Widgets/SliceViewerWidget.h"
-#include "Framework/StoneException.h"
-#include "Framework/Layers/FrameRenderer.h"
-#include "Core/Logging.h"
-
-namespace OrthancStone
-{
-  enum CachedSliceStatus
-  {
-    CachedSliceStatus_ScheduledToLoad,
-    CachedSliceStatus_GeometryLoaded,
-    CachedSliceStatus_ImageLoaded
-  };
-
-  class SmartLoader::CachedSlice : public IVolumeSlicer
-  {
-  public:
-    class RendererFactory : public LayerReadyMessage::IRendererFactory
-    {
-    private:
-      const CachedSlice&  that_;
-
-    public:
-      RendererFactory(const CachedSlice& that) :
-        that_(that)
-      {
-      }
-
-      virtual ILayerRenderer* CreateRenderer() const
-      {
-        bool isFull = (that_.effectiveQuality_ == SliceImageQuality_FullPng ||
-                       that_.effectiveQuality_ == SliceImageQuality_FullPam);
-
-        return FrameRenderer::CreateRenderer(*that_.image_, *that_.slice_, isFull);
-      }
-    };
-    
-    unsigned int                    sliceIndex_;
-    std::auto_ptr<Deprecated::Slice>            slice_;
-    boost::shared_ptr<Orthanc::ImageAccessor>   image_;
-    SliceImageQuality               effectiveQuality_;
-    CachedSliceStatus               status_;
-
-  public:
-    CachedSlice(MessageBroker& broker) :
-    IVolumeSlicer(broker)
-    {
-    }
-
-    virtual ~CachedSlice()
-    {
-    }
-
-    virtual bool GetExtent(std::vector<Vector>& points,
-                           const CoordinateSystem3D& viewportSlice)
-    {
-      // TODO: viewportSlice is not used !!!!
-      slice_->GetExtent(points);
-      return true;
-    }
-
-    virtual void ScheduleLayerCreation(const CoordinateSystem3D& viewportSlice)
-    {
-      // TODO: viewportSlice is not used !!!!
-
-      // it has already been loaded -> trigger the "layer ready" message immediately otherwise, do nothing now.  The LayerReady will be triggered
-      // once the VolumeSlicer is ready
-      if (status_ == CachedSliceStatus_ImageLoaded)
-      {
-        LOG(WARNING) << "ScheduleLayerCreation for CachedSlice (image is loaded): " << slice_->GetOrthancInstanceId();
-
-        RendererFactory factory(*this);   
-        BroadcastMessage(IVolumeSlicer::LayerReadyMessage(*this, factory, slice_->GetGeometry()));
-      }
-      else
-      {
-        LOG(WARNING) << "ScheduleLayerCreation for CachedSlice (image is not loaded yet): " << slice_->GetOrthancInstanceId();
-      }
-    }
-
-    CachedSlice* Clone() const
-    {
-      CachedSlice* output = new CachedSlice(GetBroker());
-      output->sliceIndex_ = sliceIndex_;
-      output->slice_.reset(slice_->Clone());
-      output->image_ = image_;
-      output->effectiveQuality_ = effectiveQuality_;
-      output->status_ = status_;
-
-      return output;
-    }
-
-  };
-
-
-  SmartLoader::SmartLoader(MessageBroker& broker,  
-                           OrthancApiClient& orthancApiClient) :
-    IObservable(broker),
-    IObserver(broker),
-    imageQuality_(SliceImageQuality_FullPam),
-    orthancApiClient_(orthancApiClient)
-  {
-  }
-
-  void SmartLoader::SetFrameInWidget(SliceViewerWidget& sliceViewer, 
-                                     size_t layerIndex, 
-                                     const std::string& instanceId, 
-                                     unsigned int frame)
-  {
-    // TODO: check if this frame has already been loaded or is already being loaded.
-    // - if already loaded: create a "clone" that will emit the GeometryReady/ImageReady messages "immediately"
-    //   (it can not be immediate because Observers needs to register first and this is done after this method returns)
-    // - if currently loading, we need to return an object that will observe the existing VolumeSlicer and forward
-    //   the messages to its observables
-    // in both cases, we must be carefull about objects lifecycle !!!
-
-    std::auto_ptr<IVolumeSlicer> layerSource;
-    std::string sliceKeyId = instanceId + ":" + boost::lexical_cast<std::string>(frame);
-    SmartLoader::CachedSlice* cachedSlice = NULL;
-
-    if (cachedSlices_.find(sliceKeyId) != cachedSlices_.end()) // && cachedSlices_[sliceKeyId]->status_ == CachedSliceStatus_Loaded)
-    {
-      layerSource.reset(cachedSlices_[sliceKeyId]->Clone());
-      cachedSlice = dynamic_cast<SmartLoader::CachedSlice*>(layerSource.get());
-    }
-    else
-    {
-      layerSource.reset(new DicomSeriesVolumeSlicer(IObserver::GetBroker(), orthancApiClient_));
-      dynamic_cast<DicomSeriesVolumeSlicer*>(layerSource.get())->SetImageQuality(imageQuality_);
-      layerSource->RegisterObserverCallback(new Callable<SmartLoader, IVolumeSlicer::GeometryReadyMessage>(*this, &SmartLoader::OnLayerGeometryReady));
-      layerSource->RegisterObserverCallback(new Callable<SmartLoader, DicomSeriesVolumeSlicer::FrameReadyMessage>(*this, &SmartLoader::OnFrameReady));
-      layerSource->RegisterObserverCallback(new Callable<SmartLoader, IVolumeSlicer::LayerReadyMessage>(*this, &SmartLoader::OnLayerReady));
-      dynamic_cast<DicomSeriesVolumeSlicer*>(layerSource.get())->LoadFrame(instanceId, frame);
-    }
-
-    // make sure that the widget registers the events before we trigger them
-    if (sliceViewer.GetLayerCount() == layerIndex)
-    {
-      sliceViewer.AddLayer(layerSource.release());
-    }
-    else if (sliceViewer.GetLayerCount() > layerIndex)
-    {
-      sliceViewer.ReplaceLayer(layerIndex, layerSource.release());
-    }
-    else
-    {
-      throw StoneException(ErrorCode_CanOnlyAddOneLayerAtATime);
-    }
-
-    if (cachedSlice != NULL)
-    {
-      BroadcastMessage(IVolumeSlicer::GeometryReadyMessage(*cachedSlice));
-    }
-
-  }
-
-  void SmartLoader::PreloadSlice(const std::string instanceId, 
-                                 unsigned int frame)
-  {
-    // TODO: reactivate -> need to be able to ScheduleLayerLoading in IVolumeSlicer without calling ScheduleLayerCreation
-    return;
-    // TODO: check if it is already in the cache
-
-
-
-    // create the slice in the cache with "empty" data
-    boost::shared_ptr<CachedSlice> cachedSlice(new CachedSlice(IObserver::GetBroker()));
-    cachedSlice->slice_.reset(new Deprecated::Slice(instanceId, frame));
-    cachedSlice->status_ = CachedSliceStatus_ScheduledToLoad;
-    std::string sliceKeyId = instanceId + ":" + boost::lexical_cast<std::string>(frame);
-
-    LOG(WARNING) << "Will preload: " << sliceKeyId;
-
-    cachedSlices_[sliceKeyId] = boost::shared_ptr<CachedSlice>(cachedSlice);
-
-    std::auto_ptr<IVolumeSlicer> layerSource(new DicomSeriesVolumeSlicer(IObserver::GetBroker(), orthancApiClient_));
-
-    dynamic_cast<DicomSeriesVolumeSlicer*>(layerSource.get())->SetImageQuality(imageQuality_);
-    layerSource->RegisterObserverCallback(new Callable<SmartLoader, IVolumeSlicer::GeometryReadyMessage>(*this, &SmartLoader::OnLayerGeometryReady));
-    layerSource->RegisterObserverCallback(new Callable<SmartLoader, DicomSeriesVolumeSlicer::FrameReadyMessage>(*this, &SmartLoader::OnFrameReady));
-    layerSource->RegisterObserverCallback(new Callable<SmartLoader, IVolumeSlicer::LayerReadyMessage>(*this, &SmartLoader::OnLayerReady));
-    dynamic_cast<DicomSeriesVolumeSlicer*>(layerSource.get())->LoadFrame(instanceId, frame);
-
-    // keep a ref to the VolumeSlicer until the slice is fully loaded and saved to cache
-    preloadingInstances_[sliceKeyId] = boost::shared_ptr<IVolumeSlicer>(layerSource.release());
-  }
-
-
-//  void PreloadStudy(const std::string studyId)
-//  {
-//    /* TODO */
-//  }
-
-//  void PreloadSeries(const std::string seriesId)
-//  {
-//    /* TODO */
-//  }
-
-
-  void SmartLoader::OnLayerGeometryReady(const IVolumeSlicer::GeometryReadyMessage& message)
-  {
-    const DicomSeriesVolumeSlicer& source =
-      dynamic_cast<const DicomSeriesVolumeSlicer&>(message.GetOrigin());
-
-    // save/replace the slice in cache
-    const Deprecated::Slice& slice = source.GetSlice(0); // TODO handle GetSliceCount()
-    std::string sliceKeyId = (slice.GetOrthancInstanceId() + ":" + 
-                              boost::lexical_cast<std::string>(slice.GetFrame()));
-
-    LOG(WARNING) << "Geometry ready: " << sliceKeyId;
-
-    boost::shared_ptr<CachedSlice> cachedSlice(new CachedSlice(IObserver::GetBroker()));
-    cachedSlice->slice_.reset(slice.Clone());
-    cachedSlice->effectiveQuality_ = source.GetImageQuality();
-    cachedSlice->status_ = CachedSliceStatus_GeometryLoaded;
-
-    cachedSlices_[sliceKeyId] = boost::shared_ptr<CachedSlice>(cachedSlice);
-
-    // re-emit original Layer message to observers
-    BroadcastMessage(message);
-  }
-
-
-  void SmartLoader::OnFrameReady(const DicomSeriesVolumeSlicer::FrameReadyMessage& message)
-  {
-    // save/replace the slice in cache
-    const Deprecated::Slice& slice = message.GetSlice();
-    std::string sliceKeyId = (slice.GetOrthancInstanceId() + ":" + 
-                              boost::lexical_cast<std::string>(slice.GetFrame()));
-
-    LOG(WARNING) << "Image ready: " << sliceKeyId;
-
-    boost::shared_ptr<CachedSlice> cachedSlice(new CachedSlice(IObserver::GetBroker()));
-    cachedSlice->image_.reset(Orthanc::Image::Clone(message.GetFrame()));
-    cachedSlice->effectiveQuality_ = message.GetImageQuality();
-    cachedSlice->slice_.reset(message.GetSlice().Clone());
-    cachedSlice->status_ = CachedSliceStatus_ImageLoaded;
-
-    cachedSlices_[sliceKeyId] = cachedSlice;
-
-    // re-emit original Layer message to observers
-    BroadcastMessage(message);
-  }
-
-
-  void SmartLoader::OnLayerReady(const IVolumeSlicer::LayerReadyMessage& message)
-  {
-    const DicomSeriesVolumeSlicer& source =
-      dynamic_cast<const DicomSeriesVolumeSlicer&>(message.GetOrigin());
-    
-    const Deprecated::Slice& slice = source.GetSlice(0); // TODO handle GetSliceCount() ?
-    std::string sliceKeyId = (slice.GetOrthancInstanceId() + ":" + 
-                              boost::lexical_cast<std::string>(slice.GetFrame()));
-
-    LOG(WARNING) << "Layer ready: " << sliceKeyId;
-
-    // remove the slice from the preloading slices now that it has been fully loaded and it is referenced in the cache
-    if (preloadingInstances_.find(sliceKeyId) != preloadingInstances_.end())
-    {
-      preloadingInstances_.erase(sliceKeyId);
-    }
-
-    // re-emit original Layer message to observers
-    BroadcastMessage(message);
-  }
-}
--- a/Framework/SmartLoader.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-#include <map>
-
-#include "Layers/DicomSeriesVolumeSlicer.h"
-#include "Messages/IObservable.h"
-#include "Toolbox/OrthancApiClient.h"
-
-namespace OrthancStone
-{
-  class SliceViewerWidget;
-
-  class SmartLoader : public IObservable, public IObserver
-  {
-    class CachedSlice;
-
-  protected:
-    typedef std::map<std::string, boost::shared_ptr<SmartLoader::CachedSlice> > CachedSlices;
-    CachedSlices cachedSlices_;
-
-    typedef std::map<std::string, boost::shared_ptr<IVolumeSlicer> > PreloadingInstances;
-    PreloadingInstances preloadingInstances_;
-
-    SliceImageQuality     imageQuality_;
-    OrthancApiClient&     orthancApiClient_;
-
-  public:
-    SmartLoader(MessageBroker& broker, OrthancApiClient& orthancApiClient);  // TODO: add maxPreloadStorageSizeInBytes
-
-//    void PreloadStudy(const std::string studyId);
-//    void PreloadSeries(const std::string seriesId);
-    void PreloadSlice(const std::string instanceId, unsigned int frame);
-
-    void SetImageQuality(SliceImageQuality imageQuality) { imageQuality_ = imageQuality; }
-
-    void SetFrameInWidget(SliceViewerWidget& sliceViewer, size_t layerIndex, const std::string& instanceId, unsigned int frame);
-
-    void GetFirstInstanceIdForSeries(std::string& output, const std::string& seriesId);
-
-  private:
-    void OnLayerGeometryReady(const IVolumeSlicer::GeometryReadyMessage& message);
-    void OnFrameReady(const DicomSeriesVolumeSlicer::FrameReadyMessage& message);
-    void OnLayerReady(const IVolumeSlicer::LayerReadyMessage& message);
-
-  };
-
-}
--- a/Framework/StoneEnumerations.h	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/StoneEnumerations.h	Wed May 22 16:13:46 2019 +0200
@@ -23,6 +23,22 @@
 
 #include <string>
 
+
+namespace Deprecated
+{
+  enum SliceImageQuality
+  {
+    SliceImageQuality_FullPng,  // smaller to transmit but longer to generate on Orthanc side (better choice when on low bandwidth)
+    SliceImageQuality_FullPam,  // bigger to transmit but faster to generate on Orthanc side (better choice when on localhost or LAN)
+    SliceImageQuality_Jpeg50,
+    SliceImageQuality_Jpeg90,
+    SliceImageQuality_Jpeg95,
+
+    SliceImageQuality_InternalRaw   // downloads the raw pixels data as they are stored in the DICOM file (internal use only)
+  };  
+}
+
+
 namespace OrthancStone
 {
   enum SliceOffsetMode
@@ -85,17 +101,6 @@
     KeyboardKeys_Down = 40
   };
 
-  enum SliceImageQuality
-  {
-    SliceImageQuality_FullPng,  // smaller to transmit but longer to generate on Orthanc side (better choice when on low bandwidth)
-    SliceImageQuality_FullPam,  // bigger to transmit but faster to generate on Orthanc side (better choice when on localhost or LAN)
-    SliceImageQuality_Jpeg50,
-    SliceImageQuality_Jpeg90,
-    SliceImageQuality_Jpeg95,
-
-    SliceImageQuality_InternalRaw   // downloads the raw pixels data as they are stored in the DICOM file (internal use only)
-  };
-
   enum SopClassUid
   {
     SopClassUid_Other,
@@ -115,23 +120,6 @@
     BitmapAnchor_TopRight
   };
 
-  enum ControlPointType
-  {
-    ControlPoint_TopLeftCorner = 0,
-    ControlPoint_TopRightCorner = 1,
-    ControlPoint_BottomRightCorner = 2,
-    ControlPoint_BottomLeftCorner = 3
-  };
-
-  enum PhotometricDisplayMode
-  {
-    PhotometricDisplayMode_Default,
-
-    PhotometricDisplayMode_Monochrome1,
-    PhotometricDisplayMode_Monochrome2
-  };
-
-  
   SopClassUid StringToSopClassUid(const std::string& source);
 
   void ComputeWindowing(float& targetCenter,
--- a/Framework/StoneException.h	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/StoneException.h	Wed May 22 16:13:46 2019 +0200
@@ -120,7 +120,7 @@
       std::stringstream sst; \
       sst << "Assertion failed. Condition = \"" #cond "\" Message = \"" << streamChainMessage << "\""; \
       std::string sstr = sst.str(); \
-      throw OrthancException(ErrorCode_InternalError,sstr.c_str()); \
+      throw ::Orthanc::OrthancException(::Orthanc::ErrorCode_InternalError,sstr.c_str()); \
     } else (void)0
 
 #define ORTHANC_ASSERT1(cond) \
--- a/Framework/Toolbox/BaseWebService.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,143 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2018 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 "BaseWebService.h"
-
-#include <Core/OrthancException.h>
-#include "Framework/Messages/IObservable.h"
-#include "Platforms/Generic/IOracleCommand.h"
-#include <boost/shared_ptr.hpp>
-
-namespace OrthancStone
-{
-
-
-  class BaseWebService::BaseWebServicePayload : public Orthanc::IDynamicObject
-  {
-  private:
-    std::auto_ptr< MessageHandler<IWebService::HttpRequestSuccessMessage> >   userSuccessHandler_;
-    std::auto_ptr< MessageHandler<IWebService::HttpRequestErrorMessage> >     userFailureHandler_;
-    std::auto_ptr< Orthanc::IDynamicObject>                                   userPayload_;
-
-  public:
-    BaseWebServicePayload(MessageHandler<IWebService::HttpRequestSuccessMessage>* userSuccessHandler,
-                          MessageHandler<IWebService::HttpRequestErrorMessage>* userFailureHandler,
-                          Orthanc::IDynamicObject* userPayload) :
-      userSuccessHandler_(userSuccessHandler),
-      userFailureHandler_(userFailureHandler),
-      userPayload_(userPayload)
-    {
-    }
-
-    void HandleSuccess(const IWebService::HttpRequestSuccessMessage& message) const
-    {
-      if (userSuccessHandler_.get() != NULL)
-      {
-        // recreate a success message with the user payload
-        IWebService::HttpRequestSuccessMessage successMessage(message.GetUri(),
-                                                              message.GetAnswer(),
-                                                              message.GetAnswerSize(),
-                                                              message.GetAnswerHttpHeaders(),
-                                                              userPayload_.get());
-        userSuccessHandler_->Apply(successMessage);
-      }
-      else
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-      }
-    }
-
-    void HandleFailure(const IWebService::HttpRequestErrorMessage& message) const
-    {
-      if (userFailureHandler_.get() != NULL)
-      {
-        // recreate a failure message with the user payload
-        IWebService::HttpRequestErrorMessage failureMessage(message.GetUri(),
-                                                            userPayload_.get());
-
-        userFailureHandler_->Apply(failureMessage);
-      }
-    }
-
-  };
-
-
-  void BaseWebService::GetAsync(const std::string& uri,
-                                const HttpHeaders& headers,
-                                Orthanc::IDynamicObject* payload  /* takes ownership */,
-                                MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,
-                                MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,
-                                unsigned int timeoutInSeconds)
-  {
-    if (cache_.find(uri) == cache_.end())
-    {
-      GetAsyncInternal(uri, headers,
-                       new BaseWebService::BaseWebServicePayload(successCallback, failureCallback, payload), // ownership is transfered
-                       new Callable<BaseWebService, IWebService::HttpRequestSuccessMessage>
-                       (*this, &BaseWebService::CacheAndNotifyHttpSuccess),
-                       new Callable<BaseWebService, IWebService::HttpRequestErrorMessage>
-                       (*this, &BaseWebService::NotifyHttpError),
-                       timeoutInSeconds);
-    }
-    else
-    {
-      // create a command and "post" it to the Oracle so it is executed and commited "later"
-      NotifyHttpSuccessLater(cache_[uri], payload, successCallback);
-    }
-
-  }
-
-
-
-  void BaseWebService::NotifyHttpSuccess(const IWebService::HttpRequestSuccessMessage& message)
-  {
-    if (message.HasPayload())
-    {
-      dynamic_cast<const BaseWebServicePayload&>(message.GetPayload()).HandleSuccess(message);
-    }
-    else
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-    }
-  }
-
-  void BaseWebService::CacheAndNotifyHttpSuccess(const IWebService::HttpRequestSuccessMessage& message)
-  {
-    cache_[message.GetUri()] = boost::shared_ptr<CachedHttpRequestSuccessMessage>(new CachedHttpRequestSuccessMessage(message));
-    NotifyHttpSuccess(message);
-  }
-
-  void BaseWebService::NotifyHttpError(const IWebService::HttpRequestErrorMessage& message)
-  {
-    if (message.HasPayload())
-    {
-      dynamic_cast<const BaseWebServicePayload&>(message.GetPayload()).HandleFailure(message);
-    }
-    else
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-    }
-  }
-
-
-
-
-}
--- a/Framework/Toolbox/BaseWebService.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,131 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2018 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 "IWebService.h"
-
-#include <string>
-#include <map>
-
-namespace OrthancStone
-{
-  // This is an intermediate of IWebService that implements some caching on
-  // the HTTP GET requests
-  class BaseWebService : public IWebService, public IObserver
-  {
-  public:
-    class CachedHttpRequestSuccessMessage
-    {
-    protected:
-      std::string                    uri_;
-      void*                          answer_;
-      size_t                         answerSize_;
-      IWebService::HttpHeaders       answerHeaders_;
-
-    public:
-      CachedHttpRequestSuccessMessage(const IWebService::HttpRequestSuccessMessage& message) :
-        uri_(message.GetUri()),
-        answerSize_(message.GetAnswerSize()),
-        answerHeaders_(message.GetAnswerHttpHeaders())
-      {
-        answer_ =  malloc(answerSize_);
-        memcpy(answer_, message.GetAnswer(), answerSize_);
-      }
-
-      ~CachedHttpRequestSuccessMessage()
-      {
-        free(answer_);
-      }
-
-      const std::string& GetUri() const
-      {
-        return uri_;
-      }
-
-      const void* GetAnswer() const
-      {
-        return answer_;
-      }
-
-      size_t GetAnswerSize() const
-      {
-        return answerSize_;
-      }
-
-      const IWebService::HttpHeaders&  GetAnswerHttpHeaders() const
-      {
-        return answerHeaders_;
-      }
-
-    };
-  protected:
-    class BaseWebServicePayload;
-
-    bool          cacheEnabled_;
-    std::map<std::string, boost::shared_ptr<CachedHttpRequestSuccessMessage> > cache_;  // TODO: this is currently an infinite cache !
-
-  public:
-
-    BaseWebService(MessageBroker& broker) :
-      IWebService(broker),
-      IObserver(broker),
-      cacheEnabled_(true)
-    {
-    }
-
-    virtual ~BaseWebService()
-    {
-    }
-
-    virtual void EnableCache(bool enable)
-    {
-      cacheEnabled_ = enable;
-    }
-
-    virtual void GetAsync(const std::string& uri,
-                          const HttpHeaders& headers,
-                          Orthanc::IDynamicObject* payload  /* takes ownership */,
-                          MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,
-                          MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
-                          unsigned int timeoutInSeconds = 60);
-
-  protected:
-    virtual void GetAsyncInternal(const std::string& uri,
-                          const HttpHeaders& headers,
-                          Orthanc::IDynamicObject* payload  /* takes ownership */,
-                          MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,
-                          MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
-                          unsigned int timeoutInSeconds = 60) = 0;
-
-    virtual void NotifyHttpSuccessLater(boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedHttpMessage,
-                                        Orthanc::IDynamicObject* payload, // takes ownership
-                                        MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback) = 0;
-
-  private:
-    void NotifyHttpSuccess(const IWebService::HttpRequestSuccessMessage& message);
-
-    void NotifyHttpError(const IWebService::HttpRequestErrorMessage& message);
-
-    void CacheAndNotifyHttpSuccess(const IWebService::HttpRequestSuccessMessage& message);
-
-  };
-}
--- a/Framework/Toolbox/DicomFrameConverter.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,282 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "DicomFrameConverter.h"
-
-#include "LinearAlgebra.h"
-
-#include <Core/Images/Image.h>
-#include <Core/Images/ImageProcessing.h>
-#include <Core/OrthancException.h>
-#include <Core/Toolbox.h>
-
-namespace Deprecated
-{
-  static const Orthanc::DicomTag IMAGE_TAGS[] =
-  {
-    Orthanc::DICOM_TAG_BITS_STORED,
-    Orthanc::DICOM_TAG_DOSE_GRID_SCALING,
-    Orthanc::DICOM_TAG_PHOTOMETRIC_INTERPRETATION,
-    Orthanc::DICOM_TAG_PIXEL_REPRESENTATION,
-    Orthanc::DICOM_TAG_RESCALE_INTERCEPT,
-    Orthanc::DICOM_TAG_RESCALE_SLOPE,
-    Orthanc::DICOM_TAG_WINDOW_CENTER,
-    Orthanc::DICOM_TAG_WINDOW_WIDTH
-  };
-
-  
-  void DicomFrameConverter::SetDefaultParameters()
-  {
-    isSigned_ = true;
-    isColor_ = false;
-    hasRescale_ = false;
-    rescaleIntercept_ = 0;
-    rescaleSlope_ = 1;
-    hasDefaultWindow_ = false;
-    defaultWindowCenter_ = 128;
-    defaultWindowWidth_ = 256;
-    expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale16;
-  }
-
-
-  void DicomFrameConverter::ReadParameters(const Orthanc::DicomMap& dicom)
-  {
-    SetDefaultParameters();
-
-    OrthancStone::Vector c, w;
-    if (OrthancStone::LinearAlgebra::ParseVector(c, dicom, Orthanc::DICOM_TAG_WINDOW_CENTER) &&
-        OrthancStone::LinearAlgebra::ParseVector(w, dicom, Orthanc::DICOM_TAG_WINDOW_WIDTH) &&
-        c.size() > 0 && 
-        w.size() > 0)
-    {
-      hasDefaultWindow_ = true;
-      defaultWindowCenter_ = static_cast<float>(c[0]);
-      defaultWindowWidth_ = static_cast<float>(w[0]);
-    }
-
-    int32_t tmp;
-    if (!dicom.ParseInteger32(tmp, Orthanc::DICOM_TAG_PIXEL_REPRESENTATION))
-    {
-      // Type 1 tag, must be present
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-    }
-
-    isSigned_ = (tmp == 1);
-
-    double doseGridScaling;
-    bool isRTDose = false;
-    
-    if (dicom.ParseDouble(rescaleIntercept_, Orthanc::DICOM_TAG_RESCALE_INTERCEPT) &&
-        dicom.ParseDouble(rescaleSlope_, Orthanc::DICOM_TAG_RESCALE_SLOPE))
-    {
-      hasRescale_ = true;
-    }
-    else if (dicom.ParseDouble(doseGridScaling, Orthanc::DICOM_TAG_DOSE_GRID_SCALING))
-    {
-      // This is for RT-DOSE
-      hasRescale_ = true;
-      isRTDose = true;
-      rescaleIntercept_ = 0;
-      rescaleSlope_ = doseGridScaling;
-
-      if (!dicom.ParseInteger32(tmp, Orthanc::DICOM_TAG_BITS_STORED))
-      {
-        // Type 1 tag, must be present
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-      }
-
-      switch (tmp)
-      {
-        case 16:
-          expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale16;
-          break;
-
-        case 32:
-          expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale32;
-          break;
-
-        default:
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
-      }
-    }
-
-    std::string photometric;
-    if (dicom.CopyToString(photometric, Orthanc::DICOM_TAG_PHOTOMETRIC_INTERPRETATION, false))
-    {
-      photometric = Orthanc::Toolbox::StripSpaces(photometric);
-    }
-    else
-    {
-      // Type 1 tag, must be present
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-    }
-
-    photometric_ = Orthanc::StringToPhotometricInterpretation(photometric.c_str());
-    
-    isColor_ = (photometric != "MONOCHROME1" &&
-                photometric != "MONOCHROME2");
-
-    // TODO Add more checks, e.g. on the number of bytes per value
-    // (cf. DicomImageInformation.h in Orthanc)
-
-    if (!isRTDose)
-    {
-      if (isColor_)
-      {
-        expectedPixelFormat_ = Orthanc::PixelFormat_RGB24;
-      }
-      else if (isSigned_)
-      {
-        expectedPixelFormat_ = Orthanc::PixelFormat_SignedGrayscale16;
-      }
-      else
-      {
-        expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale16;
-      }
-    }
-  }
-
-  
-  void DicomFrameConverter::ReadParameters(const OrthancPlugins::IDicomDataset& dicom)
-  {
-    Orthanc::DicomMap converted;
-
-    for (size_t i = 0; i < sizeof(IMAGE_TAGS) / sizeof(Orthanc::DicomTag); i++)
-    {
-      OrthancPlugins::DicomTag tag(IMAGE_TAGS[i].GetGroup(), IMAGE_TAGS[i].GetElement());
-    
-      std::string value;
-      if (dicom.GetStringValue(value, tag))
-      {
-        converted.SetValue(IMAGE_TAGS[i], value, false);
-      }
-    }
-
-    ReadParameters(converted);
-  }
-    
-
-  void DicomFrameConverter::ConvertFrameInplace(std::auto_ptr<Orthanc::ImageAccessor>& source) const
-  {
-    assert(sizeof(float) == 4);
-
-    if (source.get() == NULL)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-
-    if (source->GetFormat() == GetExpectedPixelFormat() &&
-        source->GetFormat() == Orthanc::PixelFormat_RGB24)
-    {
-      // No conversion has to be done, check out (*)
-      return;
-    }
-    else
-    {
-      source.reset(ConvertFrame(*source));
-    }
-  }
-
-
-  Orthanc::ImageAccessor* DicomFrameConverter::ConvertFrame(const Orthanc::ImageAccessor& source) const
-  {
-    assert(sizeof(float) == 4);
-
-    Orthanc::PixelFormat sourceFormat = source.GetFormat();
-
-    if (sourceFormat != GetExpectedPixelFormat())
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
-    }
-
-    if (sourceFormat == Orthanc::PixelFormat_RGB24)
-    {
-      // This is the case of a color image. No conversion has to be done (*)
-      std::auto_ptr<Orthanc::Image> converted(new Orthanc::Image(Orthanc::PixelFormat_RGB24, 
-                                                                 source.GetWidth(), 
-                                                                 source.GetHeight(),
-                                                                 false));
-      Orthanc::ImageProcessing::Copy(*converted, source);
-      return converted.release();
-    }
-    else
-    {
-      assert(sourceFormat == Orthanc::PixelFormat_Grayscale16 ||
-             sourceFormat == Orthanc::PixelFormat_Grayscale32 ||
-             sourceFormat == Orthanc::PixelFormat_SignedGrayscale16);
-
-      // This is the case of a grayscale frame. Convert it to Float32.
-      std::auto_ptr<Orthanc::Image> converted(new Orthanc::Image(Orthanc::PixelFormat_Float32, 
-                                                                 source.GetWidth(), 
-                                                                 source.GetHeight(),
-                                                                 false));
-      Orthanc::ImageProcessing::Convert(*converted, source);
-
-      // Correct rescale slope/intercept if need be
-      ApplyRescale(*converted, sourceFormat != Orthanc::PixelFormat_Grayscale32);
-      
-      return converted.release();
-    }
-  }
-
-
-  void DicomFrameConverter::ApplyRescale(Orthanc::ImageAccessor& image,
-                                         bool useDouble) const
-  {
-    if (image.GetFormat() != Orthanc::PixelFormat_Float32)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
-    }
-    
-    if (hasRescale_)
-    {
-      for (unsigned int y = 0; y < image.GetHeight(); y++)
-      {
-        float* p = reinterpret_cast<float*>(image.GetRow(y));
-
-        if (useDouble)
-        {
-          // Slower, accurate implementation using double
-          for (unsigned int x = 0; x < image.GetWidth(); x++, p++)
-          {
-            double value = static_cast<double>(*p);
-            *p = static_cast<float>(value * rescaleSlope_ + rescaleIntercept_);
-          }
-        }
-        else
-        {
-          // Fast, approximate implementation using float
-          for (unsigned int x = 0; x < image.GetWidth(); x++, p++)
-          {
-            *p = (*p) * static_cast<float>(rescaleSlope_) + static_cast<float>(rescaleIntercept_);
-          }
-        }
-      }
-    }
-  }
-
-  
-  double DicomFrameConverter::Apply(double x) const
-  {
-    return x * rescaleSlope_ + rescaleIntercept_;
-  }
-
-}
--- a/Framework/Toolbox/DicomFrameConverter.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,169 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include <Plugins/Samples/Common/IDicomDataset.h>
-#include <Core/DicomFormat/DicomMap.h>
-#include <Core/Images/ImageAccessor.h>
-
-#include <memory>
-
-namespace Deprecated
-{
-  /**
-   * This class is responsible for converting the pixel format of a
-   * DICOM frame coming from Orthanc, into a pixel format that is
-   * suitable for Stone, given the relevant DICOM tags:
-   * - Color frames will stay in the RGB24 format.
-   * - Grayscale frames will be converted to the Float32 format.
-   **/
-  class DicomFrameConverter
-  {
-  private:
-    bool    isSigned_;
-    bool    isColor_;
-    bool    hasRescale_;
-    double  rescaleIntercept_;
-    double  rescaleSlope_;
-    bool    hasDefaultWindow_;
-    double  defaultWindowCenter_;
-    double  defaultWindowWidth_;
-    
-    Orthanc::PhotometricInterpretation  photometric_;
-    Orthanc::PixelFormat                expectedPixelFormat_;
-
-    void SetDefaultParameters();
-
-  public:
-    DicomFrameConverter()
-    {
-      SetDefaultParameters();
-    }
-
-    ~DicomFrameConverter()
-    {
-      // TODO: check whether this dtor is called or not
-      // An MSVC warning explains that declaring an
-      // std::auto_ptr with a forward-declared type
-      // prevents its dtor from being called. Does not
-      // seem an issue here (only POD types inside), but
-      // definitely something to keep an eye on.
-      (void)0;
-    }
-
-    // AM: this is required to serialize/deserialize it
-    DicomFrameConverter(
-        bool isSigned,
-        bool isColor,
-        bool hasRescale,
-        double rescaleIntercept,
-        double rescaleSlope,
-        bool hasDefaultWindow,
-        double defaultWindowCenter,
-        double defaultWindowWidth,
-        Orthanc::PhotometricInterpretation photometric,
-        Orthanc::PixelFormat expectedPixelFormat
-        ):
-      isSigned_(isSigned),
-      isColor_(isColor),
-      hasRescale_(hasRescale),
-      rescaleIntercept_(rescaleIntercept),
-      rescaleSlope_(rescaleSlope),
-      hasDefaultWindow_(hasDefaultWindow),
-      defaultWindowCenter_(defaultWindowCenter),
-      defaultWindowWidth_(defaultWindowWidth),
-      photometric_(photometric),
-      expectedPixelFormat_(expectedPixelFormat)
-    {}
-
-    void GetParameters(bool& isSigned,
-                       bool& isColor,
-                       bool& hasRescale,
-                       double& rescaleIntercept,
-                       double& rescaleSlope,
-                       bool& hasDefaultWindow,
-                       double& defaultWindowCenter,
-                       double& defaultWindowWidth,
-                       Orthanc::PhotometricInterpretation& photometric,
-                       Orthanc::PixelFormat& expectedPixelFormat) const
-    {
-      isSigned = isSigned_;
-      isColor = isColor_;
-      hasRescale = hasRescale_;
-      rescaleIntercept = rescaleIntercept_;
-      rescaleSlope = rescaleSlope_;
-      hasDefaultWindow = hasDefaultWindow_;
-      defaultWindowCenter = defaultWindowCenter_;
-      defaultWindowWidth = defaultWindowWidth_;
-      photometric = photometric_;
-      expectedPixelFormat = expectedPixelFormat_;
-    }
-
-    Orthanc::PixelFormat GetExpectedPixelFormat() const
-    {
-      return expectedPixelFormat_;
-    }
-
-    Orthanc::PhotometricInterpretation GetPhotometricInterpretation() const
-    {
-      return photometric_;
-    }
-
-    void ReadParameters(const Orthanc::DicomMap& dicom);
-
-    void ReadParameters(const OrthancPlugins::IDicomDataset& dicom);
-
-    bool HasDefaultWindow() const
-    {
-      return hasDefaultWindow_;
-    }
-    
-    double GetDefaultWindowCenter() const
-    {
-      return defaultWindowCenter_;
-    }
-    
-    double GetDefaultWindowWidth() const
-    {
-      return defaultWindowWidth_;
-    }
-
-    double GetRescaleIntercept() const
-    {
-      return rescaleIntercept_;
-    }
-
-    double GetRescaleSlope() const
-    {
-      return rescaleSlope_;
-    }
-
-    void ConvertFrameInplace(std::auto_ptr<Orthanc::ImageAccessor>& source) const;
-
-    Orthanc::ImageAccessor* ConvertFrame(const Orthanc::ImageAccessor& source) const;
-
-    void ApplyRescale(Orthanc::ImageAccessor& image,
-                      bool useDouble) const;
-
-    double Apply(double x) const;
-  };
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Toolbox/DicomInstanceParameters.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,378 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "DicomInstanceParameters.h"
+
+#include "../Scene2D/ColorTextureSceneLayer.h"
+#include "../Scene2D/FloatTextureSceneLayer.h"
+#include "../Toolbox/GeometryToolbox.h"
+
+#include <Core/Images/Image.h>
+#include <Core/Images/ImageProcessing.h>
+#include <Core/Logging.h>
+#include <Core/OrthancException.h>
+#include <Core/Toolbox.h>
+
+
+namespace OrthancStone
+{
+  void DicomInstanceParameters::Data::ComputeDoseOffsets(const Orthanc::DicomMap& dicom)
+  {
+    // http://dicom.nema.org/medical/Dicom/2016a/output/chtml/part03/sect_C.8.8.3.2.html
+
+    {
+      std::string increment;
+
+      if (dicom.CopyToString(increment, Orthanc::DICOM_TAG_FRAME_INCREMENT_POINTER, false))
+      {
+        Orthanc::Toolbox::ToUpperCase(increment);
+        if (increment != "3004,000C")  // This is the "Grid Frame Offset Vector" tag
+        {
+          LOG(ERROR) << "RT-DOSE: Bad value for the \"FrameIncrementPointer\" tag";
+          return;
+        }
+      }
+    }
+
+    if (!LinearAlgebra::ParseVector(frameOffsets_, dicom, Orthanc::DICOM_TAG_GRID_FRAME_OFFSET_VECTOR) ||
+        frameOffsets_.size() < imageInformation_.GetNumberOfFrames())
+    {
+      LOG(ERROR) << "RT-DOSE: No information about the 3D location of some slice(s)";
+      frameOffsets_.clear();
+    }
+    else
+    {
+      if (frameOffsets_.size() >= 2)
+      {
+        thickness_ = frameOffsets_[1] - frameOffsets_[0];
+
+        if (thickness_ < 0)
+        {
+          thickness_ = -thickness_;
+        }
+      }
+    }
+  }
+
+
+  DicomInstanceParameters::Data::Data(const Orthanc::DicomMap& dicom) :
+    imageInformation_(dicom)
+  {
+    if (imageInformation_.GetNumberOfFrames() <= 0)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+    }
+
+    if (!dicom.CopyToString(studyInstanceUid_, Orthanc::DICOM_TAG_STUDY_INSTANCE_UID, false) ||
+        !dicom.CopyToString(seriesInstanceUid_, Orthanc::DICOM_TAG_SERIES_INSTANCE_UID, false) ||
+        !dicom.CopyToString(sopInstanceUid_, Orthanc::DICOM_TAG_SOP_INSTANCE_UID, false))
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+    }
+        
+    std::string s;
+    if (!dicom.CopyToString(s, Orthanc::DICOM_TAG_SOP_CLASS_UID, false))
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+    }
+    else
+    {
+      sopClassUid_ = StringToSopClassUid(s);
+    }
+
+    if (!dicom.ParseDouble(thickness_, Orthanc::DICOM_TAG_SLICE_THICKNESS))
+    {
+      thickness_ = 100.0 * std::numeric_limits<double>::epsilon();
+    }
+
+    GeometryToolbox::GetPixelSpacing(pixelSpacingX_, pixelSpacingY_, dicom);
+
+    std::string position, orientation;
+    if (dicom.CopyToString(position, Orthanc::DICOM_TAG_IMAGE_POSITION_PATIENT, false) &&
+        dicom.CopyToString(orientation, Orthanc::DICOM_TAG_IMAGE_ORIENTATION_PATIENT, false))
+    {
+      geometry_ = CoordinateSystem3D(position, orientation);
+    }
+
+    if (sopClassUid_ == SopClassUid_RTDose)
+    {
+      ComputeDoseOffsets(dicom);
+    }
+
+    isColor_ = (imageInformation_.GetPhotometricInterpretation() != Orthanc::PhotometricInterpretation_Monochrome1 &&
+                imageInformation_.GetPhotometricInterpretation() != Orthanc::PhotometricInterpretation_Monochrome2);
+
+    double doseGridScaling;
+
+    if (dicom.ParseDouble(rescaleIntercept_, Orthanc::DICOM_TAG_RESCALE_INTERCEPT) &&
+        dicom.ParseDouble(rescaleSlope_, Orthanc::DICOM_TAG_RESCALE_SLOPE))
+    {
+      hasRescale_ = true;
+    }
+    else if (dicom.ParseDouble(doseGridScaling, Orthanc::DICOM_TAG_DOSE_GRID_SCALING))
+    {
+      hasRescale_ = true;
+      rescaleIntercept_ = 0;
+      rescaleSlope_ = doseGridScaling;
+    }
+    else
+    {
+      hasRescale_ = false;
+    }
+
+    Vector c, w;
+    if (LinearAlgebra::ParseVector(c, dicom, Orthanc::DICOM_TAG_WINDOW_CENTER) &&
+        LinearAlgebra::ParseVector(w, dicom, Orthanc::DICOM_TAG_WINDOW_WIDTH) &&
+        c.size() > 0 && 
+        w.size() > 0)
+    {
+      hasDefaultWindowing_ = true;
+      defaultWindowingCenter_ = static_cast<float>(c[0]);
+      defaultWindowingWidth_ = static_cast<float>(w[0]);
+    }
+    else
+    {
+      hasDefaultWindowing_ = false;
+    }
+
+    if (sopClassUid_ == SopClassUid_RTDose)
+    {
+      switch (imageInformation_.GetBitsStored())
+      {
+        case 16:
+          expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale16;
+          break;
+
+        case 32:
+          expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale32;
+          break;
+
+        default:
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+      } 
+    }
+    else if (isColor_)
+    {
+      expectedPixelFormat_ = Orthanc::PixelFormat_RGB24;
+    }
+    else if (imageInformation_.IsSigned())
+    {
+      expectedPixelFormat_ = Orthanc::PixelFormat_SignedGrayscale16;
+    }
+    else
+    {
+      expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale16;
+    }
+  }
+
+
+  CoordinateSystem3D  DicomInstanceParameters::Data::GetFrameGeometry(unsigned int frame) const
+  {
+    if (frame == 0)
+    {
+      return geometry_;
+    }
+    else if (frame >= imageInformation_.GetNumberOfFrames())
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+    else if (sopClassUid_ == SopClassUid_RTDose)
+    {
+      if (frame >= frameOffsets_.size())
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      }
+
+      return CoordinateSystem3D(
+        geometry_.GetOrigin() + frameOffsets_[frame] * geometry_.GetNormal(),
+        geometry_.GetAxisX(),
+        geometry_.GetAxisY());
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+    }
+  }
+
+
+  bool DicomInstanceParameters::Data::IsPlaneWithinSlice(unsigned int frame,
+                                                         const CoordinateSystem3D& plane) const
+  {
+    if (frame >= imageInformation_.GetNumberOfFrames())
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+
+    CoordinateSystem3D tmp = geometry_;
+
+    if (frame != 0)
+    {
+      tmp = GetFrameGeometry(frame);
+    }
+
+    double distance;
+
+    return (CoordinateSystem3D::GetDistance(distance, tmp, plane) &&
+            distance <= thickness_ / 2.0);
+  }
+
+      
+  void DicomInstanceParameters::Data::ApplyRescale(Orthanc::ImageAccessor& image,
+                                                   bool useDouble) const
+  {
+    if (image.GetFormat() != Orthanc::PixelFormat_Float32)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
+    }
+    
+    if (hasRescale_)
+    {
+      const unsigned int width = image.GetWidth();
+      const unsigned int height = image.GetHeight();
+        
+      for (unsigned int y = 0; y < height; y++)
+      {
+        float* p = reinterpret_cast<float*>(image.GetRow(y));
+
+        if (useDouble)
+        {
+          // Slower, accurate implementation using double
+          for (unsigned int x = 0; x < width; x++, p++)
+          {
+            double value = static_cast<double>(*p);
+            *p = static_cast<float>(value * rescaleSlope_ + rescaleIntercept_);
+          }
+        }
+        else
+        {
+          // Fast, approximate implementation using float
+          for (unsigned int x = 0; x < width; x++, p++)
+          {
+            *p = (*p) * static_cast<float>(rescaleSlope_) + static_cast<float>(rescaleIntercept_);
+          }
+        }
+      }
+    }
+  }
+
+  double DicomInstanceParameters::GetRescaleIntercept() const
+  {
+    if (data_.hasRescale_)
+    {
+      return data_.rescaleIntercept_;
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+  }
+
+
+  double DicomInstanceParameters::GetRescaleSlope() const
+  {
+    if (data_.hasRescale_)
+    {
+      return data_.rescaleSlope_;
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+  }
+
+
+  float DicomInstanceParameters::GetDefaultWindowingCenter() const
+  {
+    if (data_.hasDefaultWindowing_)
+    {
+      return data_.defaultWindowingCenter_;
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+  }
+
+
+  float DicomInstanceParameters::GetDefaultWindowingWidth() const
+  {
+    if (data_.hasDefaultWindowing_)
+    {
+      return data_.defaultWindowingWidth_;
+    }
+    else
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+  }
+
+
+  TextureBaseSceneLayer* DicomInstanceParameters::CreateTexture(const Orthanc::ImageAccessor& pixelData) const
+  {
+    assert(sizeof(float) == 4);
+
+    Orthanc::PixelFormat sourceFormat = pixelData.GetFormat();
+
+    if (sourceFormat != GetExpectedPixelFormat())
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
+    }
+
+    if (sourceFormat == Orthanc::PixelFormat_RGB24)
+    {
+      // This is the case of a color image. No conversion has to be done.
+      return new ColorTextureSceneLayer(pixelData);
+    }
+    else
+    {
+      if (sourceFormat != Orthanc::PixelFormat_Grayscale16 &&
+          sourceFormat != Orthanc::PixelFormat_Grayscale32 &&
+          sourceFormat != Orthanc::PixelFormat_SignedGrayscale16)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+      }
+
+      std::auto_ptr<FloatTextureSceneLayer> texture;
+        
+      {
+        // This is the case of a grayscale frame. Convert it to Float32.
+        std::auto_ptr<Orthanc::Image> converted(new Orthanc::Image(Orthanc::PixelFormat_Float32, 
+                                                                   pixelData.GetWidth(), 
+                                                                   pixelData.GetHeight(),
+                                                                   false));
+        Orthanc::ImageProcessing::Convert(*converted, pixelData);
+
+        // Correct rescale slope/intercept if need be
+        data_.ApplyRescale(*converted, (sourceFormat == Orthanc::PixelFormat_Grayscale32));
+
+        texture.reset(new FloatTextureSceneLayer(*converted));
+      }
+
+      if (data_.hasDefaultWindowing_)
+      {
+        texture->SetCustomWindowing(data_.defaultWindowingCenter_,
+                                    data_.defaultWindowingWidth_);
+      }
+        
+      return texture.release();
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Toolbox/DicomInstanceParameters.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,185 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../StoneEnumerations.h"
+#include "../Scene2D/TextureBaseSceneLayer.h"
+#include "../Toolbox/CoordinateSystem3D.h"
+
+#include <Core/IDynamicObject.h>
+#include <Core/DicomFormat/DicomImageInformation.h>
+
+namespace OrthancStone
+{
+  class DicomInstanceParameters :
+    public Orthanc::IDynamicObject  /* to be used as a payload to SlicesSorter */
+  {
+    // This class supersedes the deprecated "DicomFrameConverter"
+
+  private:
+    struct Data   // Struct to ease the copy constructor
+    {
+      std::string                       orthancInstanceId_;
+      std::string                       studyInstanceUid_;
+      std::string                       seriesInstanceUid_;
+      std::string                       sopInstanceUid_;
+      Orthanc::DicomImageInformation    imageInformation_;
+      SopClassUid                       sopClassUid_;
+      double                            thickness_;
+      double                            pixelSpacingX_;
+      double                            pixelSpacingY_;
+      CoordinateSystem3D                geometry_;
+      Vector                            frameOffsets_;
+      bool                              isColor_;
+      bool                              hasRescale_;
+      double                            rescaleIntercept_;
+      double                            rescaleSlope_;
+      bool                              hasDefaultWindowing_;
+      float                             defaultWindowingCenter_;
+      float                             defaultWindowingWidth_;
+      Orthanc::PixelFormat              expectedPixelFormat_;
+
+      void ComputeDoseOffsets(const Orthanc::DicomMap& dicom);
+
+      Data(const Orthanc::DicomMap& dicom);
+
+      CoordinateSystem3D  GetFrameGeometry(unsigned int frame) const;
+
+      bool IsPlaneWithinSlice(unsigned int frame,
+                              const CoordinateSystem3D& plane) const;
+      
+      void ApplyRescale(Orthanc::ImageAccessor& image,
+                        bool useDouble) const;
+    };
+
+    
+    Data  data_;
+
+
+  public:
+    DicomInstanceParameters(const DicomInstanceParameters& other) :
+    data_(other.data_)
+    {
+    }
+
+    DicomInstanceParameters(const Orthanc::DicomMap& dicom) :
+      data_(dicom)
+    {
+    }
+
+    void SetOrthancInstanceIdentifier(const std::string& id)
+    {
+      data_.orthancInstanceId_ = id;
+    }
+
+    const std::string& GetOrthancInstanceIdentifier() const
+    {
+      return data_.orthancInstanceId_;
+    }
+
+    const Orthanc::DicomImageInformation& GetImageInformation() const
+    {
+      return data_.imageInformation_;
+    }
+
+    const std::string& GetStudyInstanceUid() const
+    {
+      return data_.studyInstanceUid_;
+    }
+
+    const std::string& GetSeriesInstanceUid() const
+    {
+      return data_.seriesInstanceUid_;
+    }
+
+    const std::string& GetSopInstanceUid() const
+    {
+      return data_.sopInstanceUid_;
+    }
+
+    SopClassUid GetSopClassUid() const
+    {
+      return data_.sopClassUid_;
+    }
+
+    double GetThickness() const
+    {
+      return data_.thickness_;
+    }
+
+    double GetPixelSpacingX() const
+    {
+      return data_.pixelSpacingX_;
+    }
+
+    double GetPixelSpacingY() const
+    {
+      return data_.pixelSpacingY_;
+    }
+
+    const CoordinateSystem3D&  GetGeometry() const
+    {
+      return data_.geometry_;
+    }
+
+    CoordinateSystem3D  GetFrameGeometry(unsigned int frame) const
+    {
+      return data_.GetFrameGeometry(frame);
+    }
+
+    bool IsPlaneWithinSlice(unsigned int frame,
+                            const CoordinateSystem3D& plane) const
+    {
+      return data_.IsPlaneWithinSlice(frame, plane);
+    }
+
+    bool IsColor() const
+    {
+      return data_.isColor_;
+    }
+
+    bool HasRescale() const
+    {
+      return data_.hasRescale_;
+    }
+
+    double GetRescaleIntercept() const;
+
+    double GetRescaleSlope() const;
+
+    bool HasDefaultWindowing() const
+    {
+      return data_.hasDefaultWindowing_;
+    }
+
+    float GetDefaultWindowingCenter() const;
+
+    float GetDefaultWindowingWidth() const;
+
+    Orthanc::PixelFormat GetExpectedPixelFormat() const
+    {
+      return data_.expectedPixelFormat_;
+    }
+
+    TextureBaseSceneLayer* CreateTexture(const Orthanc::ImageAccessor& pixelData) const;
+  };
+}
--- a/Framework/Toolbox/DicomStructureSet.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Toolbox/DicomStructureSet.cpp	Wed May 22 16:13:46 2019 +0200
@@ -367,9 +367,7 @@
 
   DicomStructureSet::DicomStructureSet(const OrthancPlugins::FullOrthancDataset& tags)
   {
-    using namespace OrthancPlugins;
-
-    DicomDatasetReader reader(tags);
+    OrthancPlugins::DicomDatasetReader reader(tags);
     
     size_t count, tmp;
     if (!tags.GetSequenceSize(count, DICOM_TAG_RT_ROI_OBSERVATIONS_SEQUENCE) ||
@@ -385,18 +383,18 @@
     for (size_t i = 0; i < count; i++)
     {
       structures_[i].interpretation_ = reader.GetStringValue
-        (DicomPath(DICOM_TAG_RT_ROI_OBSERVATIONS_SEQUENCE, i,
-                   DICOM_TAG_RT_ROI_INTERPRETED_TYPE),
+        (OrthancPlugins::DicomPath(DICOM_TAG_RT_ROI_OBSERVATIONS_SEQUENCE, i,
+                                   DICOM_TAG_RT_ROI_INTERPRETED_TYPE),
          "No interpretation");
 
       structures_[i].name_ = reader.GetStringValue
-        (DicomPath(DICOM_TAG_STRUCTURE_SET_ROI_SEQUENCE, i,
-                   DICOM_TAG_ROI_NAME),
+        (OrthancPlugins::DicomPath(DICOM_TAG_STRUCTURE_SET_ROI_SEQUENCE, i,
+                                   DICOM_TAG_ROI_NAME),
          "No interpretation");
 
       Vector color;
-      if (ParseVector(color, tags, DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i,
-                                             DICOM_TAG_ROI_DISPLAY_COLOR)) &&
+      if (ParseVector(color, tags, OrthancPlugins::DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i,
+                                                             DICOM_TAG_ROI_DISPLAY_COLOR)) &&
           color.size() == 3)
       {
         structures_[i].red_ = ConvertColor(color[0]);
@@ -411,8 +409,8 @@
       }
 
       size_t countSlices;
-      if (!tags.GetSequenceSize(countSlices, DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i,
-                                                       DICOM_TAG_CONTOUR_SEQUENCE)))
+      if (!tags.GetSequenceSize(countSlices, OrthancPlugins::DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i,
+                                                                       DICOM_TAG_CONTOUR_SEQUENCE)))
       {
         countSlices = 0;
       }
@@ -429,9 +427,9 @@
         unsigned int countPoints;
 
         if (!reader.GetUnsignedIntegerValue
-            (countPoints, DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i,
-                                    DICOM_TAG_CONTOUR_SEQUENCE, j,
-                                    DICOM_TAG_NUMBER_OF_CONTOUR_POINTS)))
+            (countPoints, OrthancPlugins::DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i,
+                                                    DICOM_TAG_CONTOUR_SEQUENCE, j,
+                                                    DICOM_TAG_NUMBER_OF_CONTOUR_POINTS)))
         {
           throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
         }
@@ -439,9 +437,9 @@
         //LOG(INFO) << "Parsing slice containing " << countPoints << " vertices";
 
         std::string type = reader.GetMandatoryStringValue
-          (DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i,
-                     DICOM_TAG_CONTOUR_SEQUENCE, j,
-                     DICOM_TAG_CONTOUR_GEOMETRIC_TYPE));
+          (OrthancPlugins::DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i,
+                                     DICOM_TAG_CONTOUR_SEQUENCE, j,
+                                     DICOM_TAG_CONTOUR_GEOMETRIC_TYPE));
         if (type != "CLOSED_PLANAR")
         {
           LOG(WARNING) << "Ignoring contour with geometry type: " << type;
@@ -449,24 +447,24 @@
         }
 
         size_t size;
-        if (!tags.GetSequenceSize(size, DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i,
-                                                  DICOM_TAG_CONTOUR_SEQUENCE, j,
-                                                  DICOM_TAG_CONTOUR_IMAGE_SEQUENCE)) ||
+        if (!tags.GetSequenceSize(size, OrthancPlugins::DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i,
+                                                                  DICOM_TAG_CONTOUR_SEQUENCE, j,
+                                                                  DICOM_TAG_CONTOUR_IMAGE_SEQUENCE)) ||
             size != 1)
         {
           throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);          
         }
 
         std::string sopInstanceUid = reader.GetMandatoryStringValue
-          (DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i,
-                     DICOM_TAG_CONTOUR_SEQUENCE, j,
-                     DICOM_TAG_CONTOUR_IMAGE_SEQUENCE, 0,
-                     DICOM_TAG_REFERENCED_SOP_INSTANCE_UID));
+          (OrthancPlugins::DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i,
+                                     DICOM_TAG_CONTOUR_SEQUENCE, j,
+                                     DICOM_TAG_CONTOUR_IMAGE_SEQUENCE, 0,
+                                     DICOM_TAG_REFERENCED_SOP_INSTANCE_UID));
         
         std::string slicesData = reader.GetMandatoryStringValue
-          (DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i,
-                     DICOM_TAG_CONTOUR_SEQUENCE, j,
-                     DICOM_TAG_CONTOUR_DATA));
+          (OrthancPlugins::DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i,
+                                     DICOM_TAG_CONTOUR_SEQUENCE, j,
+                                     DICOM_TAG_CONTOUR_DATA));
 
         Vector points;
         if (!LinearAlgebra::ParseVector(points, slicesData) ||
--- a/Framework/Toolbox/DownloadStack.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,196 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "DownloadStack.h"
-
-#include <Core/OrthancException.h>
-
-#include <cassert>
-
-namespace OrthancStone
-{
-  bool DownloadStack::CheckInvariants() const
-  {
-    std::vector<bool> dequeued(nodes_.size(), true);
-
-    int i = firstNode_;
-    while (i != NIL)
-    {
-      const Node& node = nodes_[i];
-
-      dequeued[i] = false;
-
-      if (node.next_ != NIL &&
-          nodes_[node.next_].prev_ != i)
-      {
-        return false;
-      }
-
-      if (node.prev_ != NIL &&
-          nodes_[node.prev_].next_ != i)
-      {
-        return false;
-      }
-
-      i = nodes_[i].next_;
-    }
-
-    for (size_t i = 0; i < nodes_.size(); i++)
-    {
-      if (nodes_[i].dequeued_ != dequeued[i])
-      {
-        return false;
-      }
-    }
-
-    return true;
-  }
-
-
-  DownloadStack::DownloadStack(unsigned int size)
-  {
-    nodes_.resize(size);
-
-    if (size == 0)
-    {
-      firstNode_ = NIL;
-    }
-    else
-    {
-      for (size_t i = 0; i < size; i++)
-      {
-        nodes_[i].prev_ = static_cast<int>(i - 1);
-        nodes_[i].next_ = static_cast<int>(i + 1);
-        nodes_[i].dequeued_ = false;
-      }
-
-      nodes_.front().prev_ = NIL;
-      nodes_.back().next_ = NIL;
-      firstNode_ = 0;
-    }
-
-    assert(CheckInvariants());
-  }
-
-
-  DownloadStack::~DownloadStack()
-  {
-    assert(CheckInvariants());    
-  }
-
-
-  bool DownloadStack::Pop(unsigned int& value)
-  {
-    assert(CheckInvariants());
-
-    if (firstNode_ == NIL)
-    {
-      for (size_t i = 0; i < nodes_.size(); i++)
-      {
-        assert(nodes_[i].dequeued_);
-      }
-
-      return false;
-    }
-    else
-    {
-      assert(firstNode_ >= 0 && firstNode_ < static_cast<int>(nodes_.size()));
-      value = firstNode_;
-
-      Node& node = nodes_[firstNode_];
-      assert(node.prev_ == NIL);
-      assert(!node.dequeued_);
-
-      node.dequeued_ = true;
-      firstNode_ = node.next_;
-
-      if (firstNode_ != NIL)
-      {
-        nodes_[firstNode_].prev_ = NIL;
-      }
-
-      return true;
-    }
-  }
-
-
-  void DownloadStack::SetTopNodeInternal(unsigned int value)
-  {
-    assert(CheckInvariants());
-
-    Node& node = nodes_[value];
-
-    if (node.dequeued_)
-    {
-      // This node has already been processed by the download thread, nothing to do
-      return;
-    }
-
-    // Remove the node from the list
-    if (node.prev_ == NIL)
-    {
-      assert(firstNode_ == static_cast<int>(value));
-      
-      // This is already the top node in the list, nothing to do
-      return;
-    }
-
-    nodes_[node.prev_].next_ = node.next_;
-
-    if (node.next_ != NIL)
-    {
-      nodes_[node.next_].prev_ = node.prev_;
-    }
-
-    // Add back the node at the top of the list
-    assert(firstNode_ != NIL);
-
-    Node& old = nodes_[firstNode_];
-    assert(old.prev_ == NIL);
-    assert(!old.dequeued_);
-    node.prev_ = NIL;
-    node.next_ = firstNode_;
-    old.prev_ = value;
-
-    firstNode_ = value;
-  }
-
-  
-  void DownloadStack::SetTopNode(unsigned int value)
-  {
-    if (value >= nodes_.size())
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-
-    SetTopNodeInternal(value);
-  }
-
-
-  void DownloadStack::SetTopNodePermissive(int value)
-  {
-    if (value >= 0 &&
-        value < static_cast<int>(nodes_.size()))
-    {
-      SetTopNodeInternal(value);
-    }
-  }
-}
--- a/Framework/Toolbox/DownloadStack.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include <vector>
-#include <boost/noncopyable.hpp>
-
-namespace OrthancStone
-{
-  class DownloadStack : public boost::noncopyable
-  {
-  private:
-    static const int NIL = -1;
-
-    // This is a doubly-linked list
-    struct Node
-    {
-      int   next_;
-      int   prev_;
-      bool  dequeued_;
-    };
-
-    std::vector<Node>   nodes_;
-    int                 firstNode_;
-
-    bool CheckInvariants() const;
-
-    void SetTopNodeInternal(unsigned int value);  
-
-  public:
-    DownloadStack(unsigned int size);
-
-    ~DownloadStack();
-
-    bool Pop(unsigned int& value);
-
-    void SetTopNode(unsigned int value);  
-
-    void SetTopNodePermissive(int value);
-  };
-}
--- a/Framework/Toolbox/FiniteProjectiveCamera.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Toolbox/FiniteProjectiveCamera.cpp	Wed May 22 16:13:46 2019 +0200
@@ -23,6 +23,7 @@
 
 #include "GeometryToolbox.h"
 #include "SubpixelReader.h"
+#include "ParallelSlices.h"
 
 #include <Core/Logging.h>
 #include <Core/OrthancException.h>
@@ -299,6 +300,7 @@
   static void ApplyRaytracerInternal(Orthanc::ImageAccessor& target,
                                      const FiniteProjectiveCamera& camera,
                                      const ImageBuffer3D& source,
+                                     const VolumeImageGeometry& geometry,
                                      VolumeProjection projection)
   {
     if (source.GetFormat() != SourceFormat ||
@@ -315,8 +317,8 @@
     LOG(WARNING) << "Output image size: " << target.GetWidth() << "x" << target.GetHeight();
     LOG(WARNING) << "Output pixel format: " << Orthanc::EnumerationToString(target.GetFormat());
 
-    std::auto_ptr<OrthancStone::ParallelSlices> slices(source.GetGeometry(projection));
-    const OrthancStone::Vector pixelSpacing = source.GetGeometry().GetVoxelDimensions(projection);
+    std::auto_ptr<OrthancStone::ParallelSlices> slices(OrthancStone::ParallelSlices::FromVolumeImage(geometry, projection));
+    const OrthancStone::Vector pixelSpacing = geometry.GetVoxelDimensions(projection);
     const unsigned int targetWidth = target.GetWidth();
     const unsigned int targetHeight = target.GetHeight();
 
@@ -422,6 +424,7 @@
 
   Orthanc::ImageAccessor*
   FiniteProjectiveCamera::ApplyRaytracer(const ImageBuffer3D& source,
+                                         const VolumeImageGeometry& geometry,
                                          Orthanc::PixelFormat targetFormat,
                                          unsigned int targetWidth,
                                          unsigned int targetHeight,
@@ -440,14 +443,14 @@
     {
       ApplyRaytracerInternal<Orthanc::PixelFormat_Grayscale16,
                              Orthanc::PixelFormat_Grayscale16, true>
-        (*target, *this, source, projection);
+        (*target, *this, source, geometry, projection);
     }
     else if (targetFormat == Orthanc::PixelFormat_Grayscale16 &&
              source.GetFormat() == Orthanc::PixelFormat_Grayscale16 && !mip)
     {
       ApplyRaytracerInternal<Orthanc::PixelFormat_Grayscale16,
                              Orthanc::PixelFormat_Grayscale16, false>
-        (*target, *this, source, projection);
+        (*target, *this, source, geometry, projection);
     }
     else
     {
--- a/Framework/Toolbox/FiniteProjectiveCamera.h	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Toolbox/FiniteProjectiveCamera.h	Wed May 22 16:13:46 2019 +0200
@@ -23,6 +23,7 @@
 
 #include "LinearAlgebra.h"
 #include "../Volumes/ImageBuffer3D.h"
+#include "../Volumes/VolumeImageGeometry.h"
 
 namespace OrthancStone
 {
@@ -109,6 +110,7 @@
     Vector ApplyGeneral(const Vector& v) const;
 
     Orthanc::ImageAccessor* ApplyRaytracer(const ImageBuffer3D& source,
+                                           const VolumeImageGeometry& geometry,
                                            Orthanc::PixelFormat targetFormat,
                                            unsigned int targetWidth,
                                            unsigned int targetHeight,
--- a/Framework/Toolbox/IDelayedCallExecutor.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2018 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/Messages/IObserver.h"
-#include "../../Framework/Messages/ICallable.h"
-
-#include <Core/IDynamicObject.h>
-#include <Core/Logging.h>
-
-#include <string>
-#include <map>
-
-namespace OrthancStone
-{
-  // The IDelayedCall executes a callback after a delay (equivalent to timeout() function in javascript).
-  class IDelayedCallExecutor : public boost::noncopyable
-  {
-  protected:
-    MessageBroker& broker_;
-    
-  public:
-    ORTHANC_STONE_DEFINE_EMPTY_MESSAGE(__FILE__, __LINE__, TimeoutMessage);
-
-    IDelayedCallExecutor(MessageBroker& broker) :
-      broker_(broker)
-    {
-    }
-
-    
-    virtual ~IDelayedCallExecutor()
-    {
-    }
-
-    
-    virtual void Schedule(MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback,
-                         unsigned int timeoutInMs = 1000) = 0;
-  };
-}
--- a/Framework/Toolbox/IWebService.cpp	Wed May 22 16:01:34 2019 +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-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "IWebService.h"
-
-#include <Core/OrthancException.h>
-
-
-namespace OrthancStone
-{
-  const Orthanc::IDynamicObject&
-  IWebService::HttpRequestSuccessMessage::GetPayload() const
-  {
-    if (HasPayload())
-    {
-      return *payload_;
-    }
-    else
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-  }
-    
-
-  const Orthanc::IDynamicObject&
-  IWebService::HttpRequestErrorMessage::GetPayload() const
-  {
-    if (HasPayload())
-    {
-      return *payload_;
-    }
-    else
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-  }
-}
--- a/Framework/Toolbox/IWebService.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,166 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "../../Framework/Messages/IObserver.h"
-#include "../../Framework/Messages/ICallable.h"
-
-#include <Core/IDynamicObject.h>
-#include <Core/Logging.h>
-
-#include <string>
-#include <map>
-
-namespace OrthancStone
-{
-  // The IWebService performs HTTP requests.
-  // Since applications can run in native or WASM environment and, since
-  // in a WASM environment, the WebService is asynchronous, the IWebservice
-  // also implements an asynchronous interface: you must schedule a request
-  // and you'll be notified when the response/error is ready.
-  class IWebService : public boost::noncopyable
-  {
-  protected:
-    MessageBroker& broker_;
-    
-  public:
-    typedef std::map<std::string, std::string> HttpHeaders;
-
-    class HttpRequestSuccessMessage : public IMessage
-    {
-      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
-
-    private:
-      const std::string&             uri_;
-      const void*                    answer_;
-      size_t                         answerSize_;
-      const HttpHeaders&             answerHeaders_;
-      const Orthanc::IDynamicObject* payload_;
-
-    public:
-      HttpRequestSuccessMessage(const std::string& uri,
-                                const void* answer,
-                                size_t answerSize,
-                                const HttpHeaders& answerHeaders,
-                                const Orthanc::IDynamicObject* payload) :
-        uri_(uri),
-        answer_(answer),
-        answerSize_(answerSize),
-        answerHeaders_(answerHeaders),
-        payload_(payload)
-      {
-      }
-
-      const std::string& GetUri() const
-      {
-        return uri_;
-      }
-
-      const void* GetAnswer() const
-      {
-        return answer_;
-      }
-
-      size_t GetAnswerSize() const
-      {
-        return answerSize_;
-      }
-
-      const HttpHeaders&  GetAnswerHttpHeaders() const
-      {
-        return answerHeaders_;
-      }
-
-      bool HasPayload() const
-      {
-        return payload_ != NULL;
-      }
-
-      const Orthanc::IDynamicObject& GetPayload() const;
-    };
-    
-
-    class HttpRequestErrorMessage : public IMessage
-    {
-      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
-
-    private:
-      const std::string&              uri_;
-      const Orthanc::IDynamicObject*  payload_;
-
-    public:
-      HttpRequestErrorMessage(const std::string& uri,
-                              const Orthanc::IDynamicObject* payload) :
-        uri_(uri),
-        payload_(payload)
-      {
-      }
-
-      const std::string& GetUri() const
-      {
-        return uri_;
-      }
-
-      bool HasPayload() const
-      {
-        return payload_ != NULL;
-      }
-
-      const Orthanc::IDynamicObject& GetPayload() const;
-    };
-
-
-    IWebService(MessageBroker& broker) :
-      broker_(broker)
-    {
-    }
-
-    
-    virtual ~IWebService()
-    {
-    }
-
-    virtual void EnableCache(bool enable) = 0;
-    
-    virtual void GetAsync(const std::string& uri,
-                          const HttpHeaders& headers,
-                          Orthanc::IDynamicObject* payload  /* takes ownership */,
-                          MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,
-                          MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
-                          unsigned int timeoutInSeconds = 60) = 0;
-
-    virtual void PostAsync(const std::string& uri,
-                           const HttpHeaders& headers,
-                           const std::string& body,
-                           Orthanc::IDynamicObject* payload  /* takes ownership */,
-                           MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,
-                           MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
-                           unsigned int timeoutInSeconds = 60) = 0;
-
-    virtual void DeleteAsync(const std::string& uri,
-                             const HttpHeaders& headers,
-                             Orthanc::IDynamicObject* payload  /* takes ownership */,
-                             MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,
-                             MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
-                             unsigned int timeoutInSeconds = 60) = 0;
-  };
-}
--- a/Framework/Toolbox/OrientedBoundingBox.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,268 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "OrientedBoundingBox.h"
-
-#include "GeometryToolbox.h"
-
-#include <Core/OrthancException.h>
-
-#include <cassert>
-
-namespace OrthancStone
-{
-  OrientedBoundingBox::OrientedBoundingBox(const ImageBuffer3D& image)
-  {
-    unsigned int n = image.GetDepth();
-    if (n < 1)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageSize);      
-    }
-
-    const CoordinateSystem3D& geometry = image.GetGeometry().GetAxialGeometry();
-    Vector dim = image.GetGeometry().GetVoxelDimensions(VolumeProjection_Axial);
-
-    u_ = geometry.GetAxisX();
-    v_ = geometry.GetAxisY();
-    w_ = geometry.GetNormal();
-
-    hu_ = static_cast<double>(image.GetWidth() * dim[0] / 2.0);
-    hv_ = static_cast<double>(image.GetHeight() * dim[1] / 2.0);
-    hw_ = static_cast<double>(image.GetDepth() * dim[2] / 2.0);
-      
-    c_ = (geometry.GetOrigin() + 
-          (hu_ - dim[0] / 2.0) * u_ +
-          (hv_ - dim[1] / 2.0) * v_ +
-          (hw_ - dim[2] / 2.0) * w_);
-  }
-
-
-  bool OrientedBoundingBox::HasIntersectionWithPlane(std::vector<Vector>& points,
-                                                     const Vector& normal,
-                                                     double d) const
-  {
-    assert(normal.size() == 3);
-
-    double r = (hu_ * fabs(boost::numeric::ublas::inner_prod(normal, u_)) +
-                hv_ * fabs(boost::numeric::ublas::inner_prod(normal, v_)) +
-                hw_ * fabs(boost::numeric::ublas::inner_prod(normal, w_)));
-
-    double s = boost::numeric::ublas::inner_prod(normal, c_) + d;
-
-    if (fabs(s) >= r)
-    {
-      // No intersection, or intersection is reduced to a single point
-      return false;
-    }
-    else
-    {
-      Vector p;
-
-      // Loop over all the 12 edges (segments) of the oriented
-      // bounding box, and check whether they intersect the plane
-        
-      // X-aligned edges
-      if (GeometryToolbox::IntersectPlaneAndSegment
-          (p, normal, d,
-           c_ - u_ * hu_ - v_ * hv_ - w_ * hw_,
-           c_ + u_ * hu_ - v_ * hv_ - w_ * hw_))
-      {
-        points.push_back(p);
-      }
-
-      if (GeometryToolbox::IntersectPlaneAndSegment
-          (p, normal, d,
-           c_ - u_ * hu_ + v_ * hv_ - w_ * hw_,
-           c_ + u_ * hu_ + v_ * hv_ - w_ * hw_))
-      {
-        points.push_back(p);
-      }
-
-      if (GeometryToolbox::IntersectPlaneAndSegment
-          (p, normal, d,
-           c_ - u_ * hu_ - v_ * hv_ + w_ * hw_,
-           c_ + u_ * hu_ - v_ * hv_ + w_ * hw_))
-      {
-        points.push_back(p);
-      }
-
-      if (GeometryToolbox::IntersectPlaneAndSegment
-          (p, normal, d,
-           c_ - u_ * hu_ + v_ * hv_ + w_ * hw_,
-           c_ + u_ * hu_ + v_ * hv_ + w_ * hw_))
-      {
-        points.push_back(p);
-      }
-
-      // Y-aligned edges
-      if (GeometryToolbox::IntersectPlaneAndSegment
-          (p, normal, d,
-           c_ - u_ * hu_ - v_ * hv_ - w_ * hw_,
-           c_ - u_ * hu_ + v_ * hv_ - w_ * hw_))
-      {
-        points.push_back(p);
-      }
-
-      if (GeometryToolbox::IntersectPlaneAndSegment
-          (p, normal, d,
-           c_ + u_ * hu_ - v_ * hv_ - w_ * hw_,
-           c_ + u_ * hu_ + v_ * hv_ - w_ * hw_))
-      {
-        points.push_back(p);
-      }
-
-      if (GeometryToolbox::IntersectPlaneAndSegment
-          (p, normal, d,
-           c_ - u_ * hu_ - v_ * hv_ + w_ * hw_,
-           c_ - u_ * hu_ + v_ * hv_ + w_ * hw_))
-      {
-        points.push_back(p);
-      }
-
-      if (GeometryToolbox::IntersectPlaneAndSegment
-          (p, normal, d,
-           c_ + u_ * hu_ - v_ * hv_ + w_ * hw_,
-           c_ + u_ * hu_ + v_ * hv_ + w_ * hw_))
-      {
-        points.push_back(p);
-      }
-
-      // Z-aligned edges
-      if (GeometryToolbox::IntersectPlaneAndSegment
-          (p, normal, d,
-           c_ - u_ * hu_ - v_ * hv_ - w_ * hw_,
-           c_ - u_ * hu_ - v_ * hv_ + w_ * hw_))
-      {
-        points.push_back(p);
-      }
-
-      if (GeometryToolbox::IntersectPlaneAndSegment
-          (p, normal, d,
-           c_ + u_ * hu_ - v_ * hv_ - w_ * hw_,
-           c_ + u_ * hu_ - v_ * hv_ + w_ * hw_))
-      {
-        points.push_back(p);
-      }
-
-      if (GeometryToolbox::IntersectPlaneAndSegment
-          (p, normal, d,
-           c_ - u_ * hu_ + v_ * hv_ - w_ * hw_,
-           c_ - u_ * hu_ + v_ * hv_ + w_ * hw_))
-      {
-        points.push_back(p);
-      }
-
-      if (GeometryToolbox::IntersectPlaneAndSegment
-          (p, normal, d,
-           c_ + u_ * hu_ + v_ * hv_ - w_ * hw_,
-           c_ + u_ * hu_ + v_ * hv_ + w_ * hw_))
-      {
-        points.push_back(p);
-      }
-
-      return true;
-    }
-  }
-
-
-  bool OrientedBoundingBox::HasIntersection(std::vector<Vector>& points,
-                                            const CoordinateSystem3D& plane) const
-  {
-    // From the vector equation of a 3D plane (specified by origin
-    // and normal), to the general equation of a 3D plane (which
-    // looses information about the origin of the coordinate system)
-    const Vector& normal = plane.GetNormal();
-    const Vector& origin = plane.GetOrigin();
-    double d = -(normal[0] * origin[0] + normal[1] * origin[1] + normal[2] * origin[2]);
-
-    return HasIntersectionWithPlane(points, normal, d);
-  }
-  
-
-  bool OrientedBoundingBox::Contains(const Vector& p) const
-  {
-    assert(p.size() == 3);
-
-    const Vector q = p - c_;
-
-    return (fabs(boost::numeric::ublas::inner_prod(q, u_)) <= hu_ &&
-            fabs(boost::numeric::ublas::inner_prod(q, v_)) <= hv_ &&
-            fabs(boost::numeric::ublas::inner_prod(q, w_)) <= hw_);
-  }
-
-  
-  void OrientedBoundingBox::FromInternalCoordinates(Vector& target,
-                                                    double x,
-                                                    double y,
-                                                    double z) const
-  {
-    target = (c_ +
-              u_ * 2.0 * hu_ * (x - 0.5) +
-              v_ * 2.0 * hv_ * (y - 0.5) +
-              w_ * 2.0 * hw_ * (z - 0.5));
-  }
-
-  
-  void OrientedBoundingBox::FromInternalCoordinates(Vector& target,
-                                                    const Vector& source) const
-  {
-    assert(source.size() == 3);
-    FromInternalCoordinates(target, source[0], source[1], source[2]);
-  }
-
-  
-  void OrientedBoundingBox::ToInternalCoordinates(Vector& target,
-                                                  const Vector& source) const
-  {
-    assert(source.size() == 3);
-    const Vector q = source - c_;
-
-    double x = boost::numeric::ublas::inner_prod(q, u_) / (2.0 * hu_) + 0.5;
-    double y = boost::numeric::ublas::inner_prod(q, v_) / (2.0 * hv_) + 0.5;
-    double z = boost::numeric::ublas::inner_prod(q, w_) / (2.0 * hw_) + 0.5;
-
-    LinearAlgebra::AssignVector(target, x, y, z);
-  }
-
-
-  bool OrientedBoundingBox::ComputeExtent(Extent2D& extent,
-                                          const CoordinateSystem3D& plane) const
-  {
-    extent.Reset();
-    
-    std::vector<Vector> points;
-    if (HasIntersection(points, plane))
-    {
-      for (size_t i = 0; i < points.size(); i++)
-      {
-        double x, y;
-        plane.ProjectPoint(x, y, points[i]);
-        extent.AddPoint(x, y);
-      }
-      
-      return true;
-    }
-    else
-    {
-      return false;
-    }
-  }
-}
--- a/Framework/Toolbox/OrientedBoundingBox.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "Extent2D.h"
-#include "LinearAlgebra.h"
-#include "../Volumes/ImageBuffer3D.h"
-
-namespace OrthancStone
-{
-  class OrientedBoundingBox : public boost::noncopyable
-  {
-  private:
-    Vector  c_;   // center
-    Vector  u_;   // normalized width vector
-    Vector  v_;   // normalized height vector
-    Vector  w_;   // normalized depth vector
-    double  hu_;  // half width
-    double  hv_;  // half height
-    double  hw_;  // half depth
-
-  public:
-    OrientedBoundingBox(const ImageBuffer3D& image);
-
-    const Vector& GetCenter() const
-    {
-      return c_;
-    }
-
-    bool HasIntersectionWithPlane(std::vector<Vector>& points,
-                                  const Vector& normal,
-                                  double d) const;
-
-    bool HasIntersection(std::vector<Vector>& points,
-                         const CoordinateSystem3D& plane) const;
-
-    bool Contains(const Vector& p) const;
-
-    void FromInternalCoordinates(Vector& target,
-                                 double x,
-                                 double y,
-                                 double z) const;
-
-    void FromInternalCoordinates(Vector& target,
-                                 const Vector& source) const;
-
-    void ToInternalCoordinates(Vector& target,
-                               const Vector& source) const;
-
-    bool ComputeExtent(Extent2D& extent,
-                       const CoordinateSystem3D& plane) const;
-  };
-}
-
--- a/Framework/Toolbox/OrthancApiClient.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,337 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-#include "OrthancApiClient.h"
-
-#include "MessagingToolbox.h"
-#include "Framework/Toolbox/MessagingToolbox.h"
-
-#include <Core/OrthancException.h>
-
-namespace OrthancStone
-{
-  const Orthanc::IDynamicObject& OrthancApiClient::JsonResponseReadyMessage::GetPayload() const
-  {
-    if (HasPayload())
-    {
-      return *payload_;
-    }
-    else
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-  }
-  
-  
-  const Orthanc::IDynamicObject& OrthancApiClient::BinaryResponseReadyMessage::GetPayload() const
-  {
-    if (HasPayload())
-    {
-      return *payload_;
-    }
-    else
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-  }
-  
-  
-  const Orthanc::IDynamicObject& OrthancApiClient::EmptyResponseReadyMessage::GetPayload() const
-  {
-    if (HasPayload())
-    {
-      return *payload_;
-    }
-    else
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-  }
-  
-  
-  class OrthancApiClient::WebServicePayload : public Orthanc::IDynamicObject
-  {
-  private:
-    std::auto_ptr< MessageHandler<EmptyResponseReadyMessage> >             emptyHandler_;
-    std::auto_ptr< MessageHandler<JsonResponseReadyMessage> >              jsonHandler_;
-    std::auto_ptr< MessageHandler<BinaryResponseReadyMessage> >            binaryHandler_;
-    std::auto_ptr< MessageHandler<IWebService::HttpRequestErrorMessage> >  failureHandler_;
-    std::auto_ptr< Orthanc::IDynamicObject >                               userPayload_;
-
-    void NotifyConversionError(const IWebService::HttpRequestSuccessMessage& message) const
-    {
-      if (failureHandler_.get() != NULL)
-      {
-        failureHandler_->Apply(IWebService::HttpRequestErrorMessage
-                               (message.GetUri(), userPayload_.get()));
-      }
-    }
-    
-  public:
-    WebServicePayload(MessageHandler<EmptyResponseReadyMessage>* handler,
-                      MessageHandler<IWebService::HttpRequestErrorMessage>* failureHandler,
-                      Orthanc::IDynamicObject* userPayload) :
-      emptyHandler_(handler),
-      failureHandler_(failureHandler),
-      userPayload_(userPayload)
-    {
-      if (handler == NULL)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-      }
-    }
-
-    WebServicePayload(MessageHandler<BinaryResponseReadyMessage>* handler,
-                      MessageHandler<IWebService::HttpRequestErrorMessage>* failureHandler,
-                      Orthanc::IDynamicObject* userPayload) :
-      binaryHandler_(handler),
-      failureHandler_(failureHandler),
-      userPayload_(userPayload)
-    {
-      if (handler == NULL)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-      }
-    }
-
-    WebServicePayload(MessageHandler<JsonResponseReadyMessage>* handler,
-                      MessageHandler<IWebService::HttpRequestErrorMessage>* failureHandler,
-                      Orthanc::IDynamicObject* userPayload) :
-      jsonHandler_(handler),
-      failureHandler_(failureHandler),
-      userPayload_(userPayload)
-    {
-      if (handler == NULL)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-      }
-    }
-
-    void HandleSuccess(const IWebService::HttpRequestSuccessMessage& message) const
-    {
-      if (emptyHandler_.get() != NULL)
-      {
-        emptyHandler_->Apply(OrthancApiClient::EmptyResponseReadyMessage
-                             (message.GetUri(), userPayload_.get()));
-      }
-      else if (binaryHandler_.get() != NULL)
-      {
-        binaryHandler_->Apply(OrthancApiClient::BinaryResponseReadyMessage
-                              (message.GetUri(), message.GetAnswer(),
-                               message.GetAnswerSize(), userPayload_.get()));
-      }
-      else if (jsonHandler_.get() != NULL)
-      {
-        Json::Value response;
-        if (MessagingToolbox::ParseJson(response, message.GetAnswer(), message.GetAnswerSize()))
-        {
-          jsonHandler_->Apply(OrthancApiClient::JsonResponseReadyMessage
-                              (message.GetUri(), response, userPayload_.get()));
-        }
-        else
-        {
-          NotifyConversionError(message);
-        }
-      }
-      else
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-      }
-    }
-
-    void HandleFailure(const IWebService::HttpRequestErrorMessage& message) const
-    {
-      if (failureHandler_.get() != NULL)
-      {
-        failureHandler_->Apply(IWebService::HttpRequestErrorMessage
-                               (message.GetUri(), userPayload_.get()));
-      }
-    }
-  };
-
-
-  OrthancApiClient::OrthancApiClient(MessageBroker& broker,
-                                     IWebService& web,
-                                     const std::string& baseUrl) :
-    IObservable(broker),
-    IObserver(broker),
-    web_(web),
-    baseUrl_(baseUrl)
-  {
-  }
-
-
-  void OrthancApiClient::GetJsonAsync(
-      const std::string& uri,
-      MessageHandler<JsonResponseReadyMessage>* successCallback,
-      MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,
-      Orthanc::IDynamicObject* payload)
-  {
-    IWebService::HttpHeaders emptyHeaders;
-    web_.GetAsync(baseUrl_ + uri,
-                  emptyHeaders,
-                  new WebServicePayload(successCallback, failureCallback, payload),
-                  new Callable<OrthancApiClient, IWebService::HttpRequestSuccessMessage>
-                  (*this, &OrthancApiClient::NotifyHttpSuccess),
-                  new Callable<OrthancApiClient, IWebService::HttpRequestErrorMessage>
-                  (*this, &OrthancApiClient::NotifyHttpError));
-  }
-
-
-  void OrthancApiClient::GetBinaryAsync(
-      const std::string& uri,
-      const std::string& contentType,
-      MessageHandler<BinaryResponseReadyMessage>* successCallback,
-      MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,
-      Orthanc::IDynamicObject* payload)
-  {
-    IWebService::HttpHeaders headers;
-    headers["Accept"] = contentType;
-    GetBinaryAsync(uri, headers, successCallback, failureCallback, payload);
-  }
-
-  void OrthancApiClient::GetBinaryAsync(
-      const std::string& uri,
-      const IWebService::HttpHeaders& headers,
-      MessageHandler<BinaryResponseReadyMessage>* successCallback,
-      MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,
-      Orthanc::IDynamicObject* payload)
-  {
-    // printf("GET [%s] [%s]\n", baseUrl_.c_str(), uri.c_str());
-
-    web_.GetAsync(baseUrl_ + uri, headers,
-                  new WebServicePayload(successCallback, failureCallback, payload),
-                  new Callable<OrthancApiClient, IWebService::HttpRequestSuccessMessage>
-                  (*this, &OrthancApiClient::NotifyHttpSuccess),
-                  new Callable<OrthancApiClient, IWebService::HttpRequestErrorMessage>
-                  (*this, &OrthancApiClient::NotifyHttpError));
-  }
-
-  
-  void OrthancApiClient::PostBinaryAsyncExpectJson(
-      const std::string& uri,
-      const std::string& body,
-      MessageHandler<JsonResponseReadyMessage>* successCallback,
-      MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,
-      Orthanc::IDynamicObject* payload)
-  {
-    web_.PostAsync(baseUrl_ + uri, IWebService::HttpHeaders(), body,
-                   new WebServicePayload(successCallback, failureCallback, payload),
-                   new Callable<OrthancApiClient, IWebService::HttpRequestSuccessMessage>
-                   (*this, &OrthancApiClient::NotifyHttpSuccess),
-                   new Callable<OrthancApiClient, IWebService::HttpRequestErrorMessage>
-                   (*this, &OrthancApiClient::NotifyHttpError));
-
-  }
-
-  void OrthancApiClient::PostBinaryAsync(
-      const std::string& uri,
-      const std::string& body)
-  {
-    web_.PostAsync(baseUrl_ + uri, IWebService::HttpHeaders(), body, NULL, NULL, NULL);
-  }
-
-  void OrthancApiClient::PostBinaryAsync(
-      const std::string& uri,
-      const std::string& body,
-      MessageHandler<EmptyResponseReadyMessage>* successCallback,
-      MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,
-      Orthanc::IDynamicObject* payload   /* takes ownership */)
-  {
-    web_.PostAsync(baseUrl_ + uri, IWebService::HttpHeaders(), body,
-                   new WebServicePayload(successCallback, failureCallback, payload),
-                   new Callable<OrthancApiClient, IWebService::HttpRequestSuccessMessage>
-                   (*this, &OrthancApiClient::NotifyHttpSuccess),
-                   new Callable<OrthancApiClient, IWebService::HttpRequestErrorMessage>
-                   (*this, &OrthancApiClient::NotifyHttpError));
-  }
-
-  void OrthancApiClient::PostJsonAsyncExpectJson(
-      const std::string& uri,
-      const Json::Value& data,
-      MessageHandler<JsonResponseReadyMessage>* successCallback,
-      MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,
-      Orthanc::IDynamicObject* payload)
-  {
-    std::string body;
-    MessagingToolbox::JsonToString(body, data);
-    return PostBinaryAsyncExpectJson(uri, body, successCallback, failureCallback, payload);
-  }
-
-  void OrthancApiClient::PostJsonAsync(
-      const std::string& uri,
-      const Json::Value& data)
-  {
-    std::string body;
-    MessagingToolbox::JsonToString(body, data);
-    return PostBinaryAsync(uri, body);
-  }
-
-  void OrthancApiClient::PostJsonAsync(
-      const std::string& uri,
-      const Json::Value& data,
-      MessageHandler<EmptyResponseReadyMessage>* successCallback,
-      MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,
-      Orthanc::IDynamicObject* payload   /* takes ownership */)
-  {
-    std::string body;
-    MessagingToolbox::JsonToString(body, data);
-    return PostBinaryAsync(uri, body, successCallback, failureCallback, payload);
-  }
-
-  void OrthancApiClient::DeleteAsync(
-      const std::string& uri,
-      MessageHandler<EmptyResponseReadyMessage>* successCallback,
-      MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,
-      Orthanc::IDynamicObject* payload)
-  {
-    web_.DeleteAsync(baseUrl_ + uri, IWebService::HttpHeaders(),
-                     new WebServicePayload(successCallback, failureCallback, payload),
-                     new Callable<OrthancApiClient, IWebService::HttpRequestSuccessMessage>
-                     (*this, &OrthancApiClient::NotifyHttpSuccess),
-                     new Callable<OrthancApiClient, IWebService::HttpRequestErrorMessage>
-                     (*this, &OrthancApiClient::NotifyHttpError));
-  }
-
-
-  void OrthancApiClient::NotifyHttpSuccess(const IWebService::HttpRequestSuccessMessage& message)
-  {
-    if (message.HasPayload())
-    {
-      dynamic_cast<const WebServicePayload&>(message.GetPayload()).HandleSuccess(message);
-    }
-    else
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-    }
-  }
-
-  void OrthancApiClient::NotifyHttpError(const IWebService::HttpRequestErrorMessage& message)
-  {
-    if (message.HasPayload())
-    {
-      dynamic_cast<const WebServicePayload&>(message.GetPayload()).HandleFailure(message);
-    }
-    else
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-    }
-  }
-}
--- a/Framework/Toolbox/OrthancApiClient.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,243 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include <boost/shared_ptr.hpp>
-#include <json/json.h>
-
-#include "IWebService.h"
-#include "../Messages/IObservable.h"
-#include "../Messages/Promise.h"
-
-namespace OrthancStone
-{
-  class OrthancApiClient :
-      public IObservable,
-      public IObserver
-  {
-  public:
-    class JsonResponseReadyMessage : public IMessage
-    {
-      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
-
-    private:
-      const std::string&              uri_;
-      const Json::Value&              json_;
-      const Orthanc::IDynamicObject*  payload_;
-
-    public:
-      JsonResponseReadyMessage(const std::string& uri,
-                               const Json::Value& json,
-                               const Orthanc::IDynamicObject* payload) :
-        uri_(uri),
-        json_(json),
-        payload_(payload)
-      {
-      }
-
-      const std::string& GetUri() const
-      {
-        return uri_;
-      }
-
-      const Json::Value& GetJson() const
-      {
-        return json_;
-      }
-
-      bool HasPayload() const
-      {
-        return payload_ != NULL;
-      }
-
-      const Orthanc::IDynamicObject& GetPayload() const;
-    };
-    
-
-    class BinaryResponseReadyMessage : public IMessage
-    {
-      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
-
-    private:
-      const std::string&              uri_;
-      const void*                     answer_;
-      size_t                          answerSize_;
-      const Orthanc::IDynamicObject*  payload_;
-
-    public:
-      BinaryResponseReadyMessage(const std::string& uri,
-                                 const void* answer,
-                                 size_t answerSize,
-                                 const Orthanc::IDynamicObject* payload) :
-        uri_(uri),
-        answer_(answer),
-        answerSize_(answerSize),
-        payload_(payload)
-      {
-      }
-
-      const std::string& GetUri() const
-      {
-        return uri_;
-      }
-
-      const void* GetAnswer() const
-      {
-        return answer_;
-      }
-
-      size_t GetAnswerSize() const
-      {
-        return answerSize_;
-      }
-
-      bool HasPayload() const
-      {
-        return payload_ != NULL;
-      }
-
-      const Orthanc::IDynamicObject& GetPayload() const;
-    };
-
-
-    class EmptyResponseReadyMessage : public IMessage
-    {
-      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
-
-    private:
-      const std::string&              uri_;
-      const Orthanc::IDynamicObject*  payload_;
-
-    public:
-      EmptyResponseReadyMessage(const std::string& uri,
-                                const Orthanc::IDynamicObject* payload) :
-        uri_(uri),
-        payload_(payload)
-      {
-      }
-
-      const std::string& GetUri() const
-      {
-        return uri_;
-      }
-
-      bool HasPayload() const
-      {
-        return payload_ != NULL;
-      }
-
-      const Orthanc::IDynamicObject& GetPayload() const;
-    };
-
-    
-
-  private:
-    class WebServicePayload;
-
-  protected:
-    IWebService&  web_;
-    std::string   baseUrl_;
-
-  public:
-    OrthancApiClient(MessageBroker& broker,
-                     IWebService& web,
-                     const std::string& baseUrl);
-    
-    virtual ~OrthancApiClient()
-    {
-    }
-
-    const std::string& GetBaseUrl() const {return baseUrl_;}
-
-    // schedule a GET request expecting a JSON response.
-    void GetJsonAsync(const std::string& uri,
-                      MessageHandler<JsonResponseReadyMessage>* successCallback,
-                      MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
-                      Orthanc::IDynamicObject* payload = NULL   /* takes ownership */);
-
-    // schedule a GET request expecting a binary response.
-    void GetBinaryAsync(const std::string& uri,
-                        const std::string& contentType,
-                        MessageHandler<BinaryResponseReadyMessage>* successCallback,
-                        MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
-                        Orthanc::IDynamicObject* payload = NULL   /* takes ownership */);
-
-    // schedule a GET request expecting a binary response.
-    void GetBinaryAsync(const std::string& uri,
-                        const IWebService::HttpHeaders& headers,
-                        MessageHandler<BinaryResponseReadyMessage>* successCallback,
-                        MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
-                        Orthanc::IDynamicObject* payload = NULL   /* takes ownership */);
-
-    // schedule a POST request expecting a JSON response.
-    void PostBinaryAsyncExpectJson(const std::string& uri,
-                                   const std::string& body,
-                                   MessageHandler<JsonResponseReadyMessage>* successCallback,
-                                   MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
-                                   Orthanc::IDynamicObject* payload = NULL   /* takes ownership */);
-
-    // schedule a POST request expecting a JSON response.
-    void PostJsonAsyncExpectJson(const std::string& uri,
-                                 const Json::Value& data,
-                                 MessageHandler<JsonResponseReadyMessage>* successCallback,
-                                 MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
-                                 Orthanc::IDynamicObject* payload = NULL   /* takes ownership */);
-
-    // schedule a POST request and don't mind the response.
-    void PostJsonAsync(const std::string& uri,
-                       const Json::Value& data);
-
-    // schedule a POST request and don't expect any response.
-    void PostJsonAsync(const std::string& uri,
-                       const Json::Value& data,
-                       MessageHandler<EmptyResponseReadyMessage>* successCallback,
-                       MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
-                       Orthanc::IDynamicObject* payload = NULL   /* takes ownership */);
-
-
-    // schedule a POST request and don't mind the response.
-    void PostBinaryAsync(const std::string& uri,
-                         const std::string& body);
-
-    // schedule a POST request and don't expect any response.
-    void PostBinaryAsync(const std::string& uri,
-                         const std::string& body,
-                         MessageHandler<EmptyResponseReadyMessage>* successCallback,
-                         MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
-                         Orthanc::IDynamicObject* payload = NULL   /* takes ownership */);
-
-    // schedule a DELETE request expecting an empty response.
-    void DeleteAsync(const std::string& uri,
-                     MessageHandler<EmptyResponseReadyMessage>* successCallback,
-                     MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
-                     Orthanc::IDynamicObject* payload = NULL   /* takes ownership */);
-
-    void NotifyHttpSuccess(const IWebService::HttpRequestSuccessMessage& message);
-
-    void NotifyHttpError(const IWebService::HttpRequestErrorMessage& message);
-
-  private:
-    void HandleFromCache(const std::string& uri,
-                         const IWebService::HttpHeaders& headers,
-                         Orthanc::IDynamicObject* payload /* takes ownership */);
-  };
-}
--- a/Framework/Toolbox/OrthancSlicesLoader.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,904 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "OrthancSlicesLoader.h"
-
-#include "MessagingToolbox.h"
-
-#include <Core/Compression/GzipCompressor.h>
-#include <Core/Endianness.h>
-#include <Core/Images/Image.h>
-#include <Core/Images/ImageProcessing.h>
-#include <Core/Images/JpegReader.h>
-#include <Core/Images/PngReader.h>
-#include <Core/Images/PamReader.h>
-#include <Core/Logging.h>
-#include <Core/OrthancException.h>
-#include <Core/Toolbox.h>
-#include <Plugins/Samples/Common/FullOrthancDataset.h>
-
-#include <boost/lexical_cast.hpp>
-
-
-
-/**
- * TODO This is a SLOW implementation of base64 decoding, because
- * "Orthanc::Toolbox::DecodeBase64()" does not work properly with
- * WASM. UNDERSTAND WHY.
- * https://stackoverflow.com/a/34571089/881731
- **/
-static std::string base64_decode(const std::string &in)
-{
-  std::string out;
-  
-  std::vector<int> T(256,-1);
-  for (int i=0; i<64; i++) T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i;
-  
-  int val=0, valb=-8;
-  for (size_t i = 0; i < in.size(); i++) {
-    unsigned char c = in[i];
-    if (T[c] == -1) break;
-    val = (val<<6) + T[c];
-    valb += 6;
-    if (valb>=0) {
-      out.push_back(char((val>>valb)&0xFF));
-      valb-=8;
-    }
-  }
-  return out;
-}
-
-
-
-namespace OrthancStone
-{
-  class OrthancSlicesLoader::Operation : public Orthanc::IDynamicObject
-  {
-  private:
-    Mode               mode_;
-    unsigned int       frame_;
-    unsigned int       sliceIndex_;
-    const Deprecated::Slice*       slice_;
-    std::string        instanceId_;
-    SliceImageQuality  quality_;
-
-    Operation(Mode mode) :
-      mode_(mode)
-    {
-    }
-
-  public:
-    Mode GetMode() const
-    {
-      return mode_;
-    }
-
-    SliceImageQuality GetQuality() const
-    {
-      assert(mode_ == Mode_LoadImage ||
-             mode_ == Mode_LoadRawImage);
-      return quality_;
-    }
-
-    unsigned int GetSliceIndex() const
-    {
-      assert(mode_ == Mode_LoadImage ||
-             mode_ == Mode_LoadRawImage);
-      return sliceIndex_;
-    }
-
-    const Deprecated::Slice& GetSlice() const
-    {
-      assert(mode_ == Mode_LoadImage ||
-             mode_ == Mode_LoadRawImage);
-      assert(slice_ != NULL);
-      return *slice_;
-    }
-
-    unsigned int GetFrame() const
-    {
-      assert(mode_ == Mode_FrameGeometry);
-      return frame_;
-    }
-
-    const std::string& GetInstanceId() const
-    {
-      assert(mode_ == Mode_FrameGeometry ||
-             mode_ == Mode_InstanceGeometry);
-      return instanceId_;
-    }
-
-    static Operation* DownloadInstanceGeometry(const std::string& instanceId)
-    {
-      std::auto_ptr<Operation> operation(new Operation(Mode_InstanceGeometry));
-      operation->instanceId_ = instanceId;
-      return operation.release();
-    }
-
-    static Operation* DownloadFrameGeometry(const std::string& instanceId,
-                                            unsigned int frame)
-    {
-      std::auto_ptr<Operation> operation(new Operation(Mode_FrameGeometry));
-      operation->instanceId_ = instanceId;
-      operation->frame_ = frame;
-      return operation.release();
-    }
-
-    static Operation* DownloadSliceImage(unsigned int  sliceIndex,
-                                         const Deprecated::Slice&  slice,
-                                         SliceImageQuality quality)
-    {
-      std::auto_ptr<Operation> tmp(new Operation(Mode_LoadImage));
-      tmp->sliceIndex_ = sliceIndex;
-      tmp->slice_ = &slice;
-      tmp->quality_ = quality;
-      return tmp.release();
-    }
-
-    static Operation* DownloadSliceRawImage(unsigned int  sliceIndex,
-                                            const Deprecated::Slice&  slice)
-    {
-      std::auto_ptr<Operation> tmp(new Operation(Mode_LoadRawImage));
-      tmp->sliceIndex_ = sliceIndex;
-      tmp->slice_ = &slice;
-      tmp->quality_ = SliceImageQuality_InternalRaw;
-      return tmp.release();
-    }
-
-    static Operation* DownloadDicomFile(const Deprecated::Slice&  slice)
-    {
-      std::auto_ptr<Operation> tmp(new Operation(Mode_LoadDicomFile));
-      tmp->slice_ = &slice;
-      return tmp.release();
-    }
-
-  };
-
-  void OrthancSlicesLoader::NotifySliceImageSuccess(const Operation& operation,
-                                                    const Orthanc::ImageAccessor& image)
-  {
-    OrthancSlicesLoader::SliceImageReadyMessage msg
-      (*this, operation.GetSliceIndex(), operation.GetSlice(), image, operation.GetQuality());
-    BroadcastMessage(msg);
-  }
-  
-  
-  void OrthancSlicesLoader::NotifySliceImageError(const Operation& operation)
-  {
-    OrthancSlicesLoader::SliceImageErrorMessage msg
-      (*this, operation.GetSliceIndex(), operation.GetSlice(), operation.GetQuality());
-    BroadcastMessage(msg);
-  }
-  
-  
-  void OrthancSlicesLoader::SortAndFinalizeSlices()
-  {
-    bool ok = slices_.Sort();
-    
-    state_ = State_GeometryReady;
-    
-    if (ok)
-    {
-      LOG(INFO) << "Loaded a series with " << slices_.GetSlicesCount() << " slice(s)";
-      BroadcastMessage(SliceGeometryReadyMessage(*this));
-    }
-    else
-    {
-      LOG(ERROR) << "This series is empty";
-      BroadcastMessage(SliceGeometryErrorMessage(*this));
-    }
-  }
-  
-  void OrthancSlicesLoader::OnGeometryError(const IWebService::HttpRequestErrorMessage& message)
-  {
-    BroadcastMessage(SliceGeometryErrorMessage(*this));
-    state_ = State_Error;
-  }
-
-  void OrthancSlicesLoader::OnSliceImageError(const IWebService::HttpRequestErrorMessage& message)
-  {
-    NotifySliceImageError(dynamic_cast<const Operation&>(message.GetPayload()));
-    state_ = State_Error;
-  }
-
-  void OrthancSlicesLoader::ParseSeriesGeometry(const OrthancApiClient::JsonResponseReadyMessage& message)
-  {
-    const Json::Value& series = message.GetJson();
-    Json::Value::Members instances = series.getMemberNames();
-    
-    slices_.Reserve(instances.size());
-    
-    for (size_t i = 0; i < instances.size(); i++)
-    {
-      OrthancPlugins::FullOrthancDataset dataset(series[instances[i]]);
-      
-      Orthanc::DicomMap dicom;
-      MessagingToolbox::ConvertDataset(dicom, dataset);
-      
-      unsigned int frames;
-      if (!dicom.ParseUnsignedInteger32(frames, Orthanc::DICOM_TAG_NUMBER_OF_FRAMES))
-      {
-        frames = 1;
-      }
-      
-      for (unsigned int frame = 0; frame < frames; frame++)
-      {
-        std::auto_ptr<Deprecated::Slice> slice(new Deprecated::Slice);
-        if (slice->ParseOrthancFrame(dicom, instances[i], frame))
-        {
-          CoordinateSystem3D geometry = slice->GetGeometry();
-          slices_.AddSlice(geometry, slice.release());
-        }
-        else
-        {
-          LOG(WARNING) << "Skipping invalid frame " << frame << " within instance " << instances[i];
-        }
-      }
-    }
-    
-    SortAndFinalizeSlices();
-  }
-  
-  void OrthancSlicesLoader::ParseInstanceGeometry(const OrthancApiClient::JsonResponseReadyMessage& message)
-  {
-    const Json::Value& tags = message.GetJson();
-    const std::string& instanceId = dynamic_cast<const OrthancSlicesLoader::Operation&>(message.GetPayload()).GetInstanceId();
-
-    OrthancPlugins::FullOrthancDataset dataset(tags);
-    
-    Orthanc::DicomMap dicom;
-    MessagingToolbox::ConvertDataset(dicom, dataset);
-
-    unsigned int frames;
-    if (!dicom.ParseUnsignedInteger32(frames, Orthanc::DICOM_TAG_NUMBER_OF_FRAMES))
-    {
-      frames = 1;
-    }
-    
-    LOG(INFO) << "Instance " << instanceId << " contains " << frames << " frame(s)";
-    
-    for (unsigned int frame = 0; frame < frames; frame++)
-    {
-      std::auto_ptr<Deprecated::Slice> slice(new Deprecated::Slice);
-      if (slice->ParseOrthancFrame(dicom, instanceId, frame))
-      {
-        CoordinateSystem3D geometry = slice->GetGeometry();
-        slices_.AddSlice(geometry, slice.release());
-      }
-      else
-      {
-        LOG(WARNING) << "Skipping invalid multi-frame instance " << instanceId;
-        BroadcastMessage(SliceGeometryErrorMessage(*this));
-        return;
-      }
-    }
-    
-    SortAndFinalizeSlices();
-  }
-  
-  
-  void OrthancSlicesLoader::ParseFrameGeometry(const OrthancApiClient::JsonResponseReadyMessage& message)
-  {
-    const Json::Value& tags = message.GetJson();
-    const std::string& instanceId = dynamic_cast<const OrthancSlicesLoader::Operation&>(message.GetPayload()).GetInstanceId();
-    unsigned int frame = dynamic_cast<const OrthancSlicesLoader::Operation&>(message.GetPayload()).GetFrame();
-
-    OrthancPlugins::FullOrthancDataset dataset(tags);
-    
-    state_ = State_GeometryReady;
-    
-    Orthanc::DicomMap dicom;
-    MessagingToolbox::ConvertDataset(dicom, dataset);
-    
-    std::auto_ptr<Deprecated::Slice> slice(new Deprecated::Slice);
-    if (slice->ParseOrthancFrame(dicom, instanceId, frame))
-    {
-      LOG(INFO) << "Loaded instance geometry " << instanceId;
-
-      CoordinateSystem3D geometry = slice->GetGeometry();
-      slices_.AddSlice(geometry, slice.release());
-      
-      BroadcastMessage(SliceGeometryReadyMessage(*this));
-    }
-    else
-    {
-      LOG(WARNING) << "Skipping invalid instance " << instanceId;
-      BroadcastMessage(SliceGeometryErrorMessage(*this));
-    }
-  }
-  
-  
-  void OrthancSlicesLoader::ParseSliceImagePng(const OrthancApiClient::BinaryResponseReadyMessage& message)
-  {
-    const Operation& operation = dynamic_cast<const OrthancSlicesLoader::Operation&>(message.GetPayload());
-    std::auto_ptr<Orthanc::ImageAccessor>  image;
-    
-    try
-    {
-      image.reset(new Orthanc::PngReader);
-      dynamic_cast<Orthanc::PngReader&>(*image).ReadFromMemory(message.GetAnswer(), message.GetAnswerSize());
-    }
-    catch (Orthanc::OrthancException&)
-    {
-      NotifySliceImageError(operation);
-      return;
-    }
-    
-    if (image->GetWidth() != operation.GetSlice().GetWidth() ||
-        image->GetHeight() != operation.GetSlice().GetHeight())
-    {
-      NotifySliceImageError(operation);
-      return;
-    }
-
-    if (operation.GetSlice().GetConverter().GetExpectedPixelFormat() ==
-        Orthanc::PixelFormat_SignedGrayscale16)
-    {
-      if (image->GetFormat() == Orthanc::PixelFormat_Grayscale16)
-      {
-        image->SetFormat(Orthanc::PixelFormat_SignedGrayscale16);
-      }
-      else
-      {
-        NotifySliceImageError(operation);
-        return;
-      }
-    }
-    
-    NotifySliceImageSuccess(operation, *image);
-  }
-  
-  void OrthancSlicesLoader::ParseSliceImagePam(const OrthancApiClient::BinaryResponseReadyMessage& message)
-  {
-    const Operation& operation = dynamic_cast<const OrthancSlicesLoader::Operation&>(message.GetPayload());
-    std::auto_ptr<Orthanc::ImageAccessor>  image;
-
-    try
-    {
-      image.reset(new Orthanc::PamReader);
-      dynamic_cast<Orthanc::PamReader&>(*image).ReadFromMemory(message.GetAnswer(), message.GetAnswerSize());
-    }
-    catch (Orthanc::OrthancException&)
-    {
-      NotifySliceImageError(operation);
-      return;
-    }
-
-    if (image->GetWidth() != operation.GetSlice().GetWidth() ||
-        image->GetHeight() != operation.GetSlice().GetHeight())
-    {
-      NotifySliceImageError(operation);
-      return;
-    }
-
-    if (operation.GetSlice().GetConverter().GetExpectedPixelFormat() ==
-        Orthanc::PixelFormat_SignedGrayscale16)
-    {
-      if (image->GetFormat() == Orthanc::PixelFormat_Grayscale16)
-      {
-        image->SetFormat(Orthanc::PixelFormat_SignedGrayscale16);
-      }
-      else
-      {
-        NotifySliceImageError(operation);
-        return;
-      }
-    }
-
-    NotifySliceImageSuccess(operation, *image);
-  }
-
-
-  void OrthancSlicesLoader::ParseSliceImageJpeg(const OrthancApiClient::JsonResponseReadyMessage& message)
-  {
-    const Operation& operation = dynamic_cast<const OrthancSlicesLoader::Operation&>(message.GetPayload());
-
-    const Json::Value& encoded = message.GetJson();
-    if (encoded.type() != Json::objectValue ||
-        !encoded.isMember("Orthanc") ||
-        encoded["Orthanc"].type() != Json::objectValue)
-    {
-      NotifySliceImageError(operation);
-      return;
-    }
-    
-    const Json::Value& info = encoded["Orthanc"];
-    if (!info.isMember("PixelData") ||
-        !info.isMember("Stretched") ||
-        !info.isMember("Compression") ||
-        info["Compression"].type() != Json::stringValue ||
-        info["PixelData"].type() != Json::stringValue ||
-        info["Stretched"].type() != Json::booleanValue ||
-        info["Compression"].asString() != "Jpeg")
-    {
-      NotifySliceImageError(operation);
-      return;
-    }
-    
-    bool isSigned = false;
-    bool isStretched = info["Stretched"].asBool();
-    
-    if (info.isMember("IsSigned"))
-    {
-      if (info["IsSigned"].type() != Json::booleanValue)
-      {
-        NotifySliceImageError(operation);
-        return;
-      }
-      else
-      {
-        isSigned = info["IsSigned"].asBool();
-      }
-    }
-    
-    std::auto_ptr<Orthanc::ImageAccessor> reader;
-    
-    {
-      std::string jpeg;
-      //Orthanc::Toolbox::DecodeBase64(jpeg, info["PixelData"].asString());
-      jpeg = base64_decode(info["PixelData"].asString());
-      
-      try
-      {
-        reader.reset(new Orthanc::JpegReader);
-        dynamic_cast<Orthanc::JpegReader&>(*reader).ReadFromMemory(jpeg);
-      }
-      catch (Orthanc::OrthancException&)
-      {
-        NotifySliceImageError(operation);
-        return;
-      }
-    }
-    
-    Orthanc::PixelFormat expectedFormat =
-      operation.GetSlice().GetConverter().GetExpectedPixelFormat();
-    
-    if (reader->GetFormat() == Orthanc::PixelFormat_RGB24)  // This is a color image
-    {
-      if (expectedFormat != Orthanc::PixelFormat_RGB24)
-      {
-        NotifySliceImageError(operation);
-        return;
-      }
-      
-      if (isSigned || isStretched)
-      {
-        NotifySliceImageError(operation);
-        return;
-      }
-      else
-      {
-        NotifySliceImageSuccess(operation, *reader);
-        return;
-      }
-    }
-    
-    if (reader->GetFormat() != Orthanc::PixelFormat_Grayscale8)
-    {
-      NotifySliceImageError(operation);
-      return;
-    }
-    
-    if (!isStretched)
-    {
-      if (expectedFormat != reader->GetFormat())
-      {
-        NotifySliceImageError(operation);
-        return;
-      }
-      else
-      {
-        NotifySliceImageSuccess(operation, *reader);
-        return;
-      }
-    }
-    
-    int32_t stretchLow = 0;
-    int32_t stretchHigh = 0;
-    
-    if (!info.isMember("StretchLow") ||
-        !info.isMember("StretchHigh") ||
-        info["StretchLow"].type() != Json::intValue ||
-        info["StretchHigh"].type() != Json::intValue)
-    {
-      NotifySliceImageError(operation);
-      return;
-    }
-    
-    stretchLow = info["StretchLow"].asInt();
-    stretchHigh = info["StretchHigh"].asInt();
-    
-    if (stretchLow < -32768 ||
-        stretchHigh > 65535 ||
-        (stretchLow < 0 && stretchHigh > 32767))
-    {
-      // This range cannot be represented with a uint16_t or an int16_t
-      NotifySliceImageError(operation);
-      return;
-    }
-    
-    // Decode a grayscale JPEG 8bpp image coming from the Web viewer
-    std::auto_ptr<Orthanc::ImageAccessor> image
-      (new Orthanc::Image(expectedFormat, reader->GetWidth(), reader->GetHeight(), false));
-
-    Orthanc::ImageProcessing::Convert(*image, *reader);
-    reader.reset();
-    
-    float scaling = static_cast<float>(stretchHigh - stretchLow) / 255.0f;
-    
-    if (!LinearAlgebra::IsCloseToZero(scaling))
-    {
-      float offset = static_cast<float>(stretchLow) / scaling;
-      Orthanc::ImageProcessing::ShiftScale(*image, offset, scaling, true);
-    }
-    
-    NotifySliceImageSuccess(operation, *image);
-  }
-
-
-  class StringImage : public Orthanc::ImageAccessor
-  {
-  private:
-    std::string  buffer_;
-    
-  public:
-    StringImage(Orthanc::PixelFormat format,
-                unsigned int width,
-                unsigned int height,
-                std::string& buffer)
-    {
-      if (buffer.size() != Orthanc::GetBytesPerPixel(format) * width * height)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
-      }
-      
-      buffer_.swap(buffer);  // The source buffer is now empty
-      
-      void* data = (buffer_.empty() ? NULL : &buffer_[0]);
-      
-      AssignWritable(format, width, height,
-                     Orthanc::GetBytesPerPixel(format) * width, data);
-    }
-  };
-  
-  void OrthancSlicesLoader::ParseSliceRawImage(const OrthancApiClient::BinaryResponseReadyMessage& message)
-  {
-    const Operation& operation = dynamic_cast<const OrthancSlicesLoader::Operation&>(message.GetPayload());
-    Orthanc::GzipCompressor compressor;
-    
-    std::string raw;
-    compressor.Uncompress(raw, message.GetAnswer(), message.GetAnswerSize());
-    
-    const Orthanc::DicomImageInformation& info = operation.GetSlice().GetImageInformation();
-    
-    if (info.GetBitsAllocated() == 32 &&
-        info.GetBitsStored() == 32 &&
-        info.GetHighBit() == 31 &&
-        info.GetChannelCount() == 1 &&
-        !info.IsSigned() &&
-        info.GetPhotometricInterpretation() == Orthanc::PhotometricInterpretation_Monochrome2 &&
-        raw.size() == info.GetWidth() * info.GetHeight() * 4)
-    {
-      // This is the case of RT-DOSE (uint32_t values)
-      
-      std::auto_ptr<Orthanc::ImageAccessor> image
-        (new StringImage(Orthanc::PixelFormat_Grayscale32, info.GetWidth(),
-                         info.GetHeight(), raw));
-      
-      // TODO - Only for big endian
-      for (unsigned int y = 0; y < image->GetHeight(); y++)
-      {
-        uint32_t *p = reinterpret_cast<uint32_t*>(image->GetRow(y));
-        for (unsigned int x = 0; x < image->GetWidth(); x++, p++)
-        {
-          *p = le32toh(*p);
-        }
-      }
-      
-      NotifySliceImageSuccess(operation, *image);
-    }
-    else if (info.GetBitsAllocated() == 16 &&
-             info.GetBitsStored() == 16 &&
-             info.GetHighBit() == 15 &&
-             info.GetChannelCount() == 1 &&
-             !info.IsSigned() &&
-             info.GetPhotometricInterpretation() == Orthanc::PhotometricInterpretation_Monochrome2 &&
-             raw.size() == info.GetWidth() * info.GetHeight() * 2)
-    {
-      std::auto_ptr<Orthanc::ImageAccessor> image
-        (new StringImage(Orthanc::PixelFormat_Grayscale16, info.GetWidth(),
-                         info.GetHeight(), raw));
-      
-      // TODO - Big endian ?
-      
-      NotifySliceImageSuccess(operation, *image);
-    }
-    else
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
-    }
-
-  }
-  
-  
-  OrthancSlicesLoader::OrthancSlicesLoader(MessageBroker& broker,
-                                           OrthancApiClient& orthanc) :
-    IObservable(broker),
-    IObserver(broker),
-    orthanc_(orthanc),
-    state_(State_Initialization)
-  {
-  }
-  
-  
-  void OrthancSlicesLoader::ScheduleLoadSeries(const std::string& seriesId)
-  {
-    if (state_ != State_Initialization)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-    else
-    {
-      state_ = State_LoadingGeometry;
-      orthanc_.GetJsonAsync("/series/" + seriesId + "/instances-tags",
-                            new Callable<OrthancSlicesLoader, OrthancApiClient::JsonResponseReadyMessage>(*this, &OrthancSlicesLoader::ParseSeriesGeometry),
-                            new Callable<OrthancSlicesLoader, IWebService::HttpRequestErrorMessage>(*this, &OrthancSlicesLoader::OnGeometryError),
-                            NULL);
-    }
-  }
-  
-  void OrthancSlicesLoader::ScheduleLoadInstance(const std::string& instanceId)
-  {
-    if (state_ != State_Initialization)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-    else
-    {
-      state_ = State_LoadingGeometry;
-      
-      // Tag "3004-000c" is "Grid Frame Offset Vector", which is
-      // mandatory to read RT DOSE, but is too long to be returned by default
-      orthanc_.GetJsonAsync("/instances/" + instanceId + "/tags?ignore-length=3004-000c",
-                            new Callable<OrthancSlicesLoader, OrthancApiClient::JsonResponseReadyMessage>(*this, &OrthancSlicesLoader::ParseInstanceGeometry),
-                            new Callable<OrthancSlicesLoader, IWebService::HttpRequestErrorMessage>(*this, &OrthancSlicesLoader::OnGeometryError),
-                            Operation::DownloadInstanceGeometry(instanceId));
-    }
-  }
-  
-  
-  void OrthancSlicesLoader::ScheduleLoadFrame(const std::string& instanceId,
-                                              unsigned int frame)
-  {
-    if (state_ != State_Initialization)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-    else
-    {
-      state_ = State_LoadingGeometry;
-
-      orthanc_.GetJsonAsync("/instances/" + instanceId + "/tags",
-                            new Callable<OrthancSlicesLoader, OrthancApiClient::JsonResponseReadyMessage>(*this, &OrthancSlicesLoader::ParseFrameGeometry),
-                            new Callable<OrthancSlicesLoader, IWebService::HttpRequestErrorMessage>(*this, &OrthancSlicesLoader::OnGeometryError),
-                            Operation::DownloadFrameGeometry(instanceId, frame));
-    }
-  }
-  
-  
-  bool OrthancSlicesLoader::IsGeometryReady() const
-  {
-    return state_ == State_GeometryReady;
-  }
-  
-  
-  size_t OrthancSlicesLoader::GetSlicesCount() const
-  {
-    if (state_ != State_GeometryReady)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-    
-    return slices_.GetSlicesCount();
-  }
-  
-  
-  const Deprecated::Slice& OrthancSlicesLoader::GetSlice(size_t index) const
-  {
-    if (state_ != State_GeometryReady)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-
-    return dynamic_cast<const Deprecated::Slice&>(slices_.GetSlicePayload(index));
-  }
-  
-  
-  bool OrthancSlicesLoader::LookupSlice(size_t& index,
-                                        const CoordinateSystem3D& plane) const
-  {
-    if (state_ != State_GeometryReady)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-
-    double distance;
-    return (slices_.LookupClosestSlice(index, distance, plane) &&
-            distance <= GetSlice(index).GetThickness() / 2.0);
-  }
-  
-  
-  void OrthancSlicesLoader::ScheduleSliceImagePng(const Deprecated::Slice& slice,
-                                                  size_t index)
-  {
-    std::string uri = ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" +
-                       boost::lexical_cast<std::string>(slice.GetFrame()));
-    
-    switch (slice.GetConverter().GetExpectedPixelFormat())
-    {
-      case Orthanc::PixelFormat_RGB24:
-        uri += "/preview";
-        break;
-      
-      case Orthanc::PixelFormat_Grayscale16:
-        uri += "/image-uint16";
-        break;
-      
-      case Orthanc::PixelFormat_SignedGrayscale16:
-        uri += "/image-int16";
-        break;
-      
-      default:
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-    
-    orthanc_.GetBinaryAsync(uri, "image/png",
-      new Callable<OrthancSlicesLoader, 
-        OrthancApiClient::BinaryResponseReadyMessage>
-          (*this, &OrthancSlicesLoader::ParseSliceImagePng),
-      new Callable<OrthancSlicesLoader, 
-        IWebService::HttpRequestErrorMessage>
-          (*this, &OrthancSlicesLoader::OnSliceImageError),
-      Operation::DownloadSliceImage(
-        static_cast<unsigned int>(index), slice, SliceImageQuality_FullPng));
-}
-  
-  void OrthancSlicesLoader::ScheduleSliceImagePam(const Deprecated::Slice& slice,
-                                                  size_t index)
-  {
-    std::string uri = 
-      ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" +
-      boost::lexical_cast<std::string>(slice.GetFrame()));
-
-    switch (slice.GetConverter().GetExpectedPixelFormat())
-    {
-      case Orthanc::PixelFormat_RGB24:
-        uri += "/preview";
-        break;
-
-      case Orthanc::PixelFormat_Grayscale16:
-        uri += "/image-uint16";
-        break;
-
-      case Orthanc::PixelFormat_SignedGrayscale16:
-        uri += "/image-int16";
-        break;
-
-      default:
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-
-    orthanc_.GetBinaryAsync(uri, "image/x-portable-arbitrarymap",
-      new Callable<OrthancSlicesLoader, 
-        OrthancApiClient::BinaryResponseReadyMessage>
-          (*this, &OrthancSlicesLoader::ParseSliceImagePam),
-      new Callable<OrthancSlicesLoader, 
-        IWebService::HttpRequestErrorMessage>
-          (*this, &OrthancSlicesLoader::OnSliceImageError),
-      Operation::DownloadSliceImage(static_cast<unsigned int>(index), 
-                                    slice, SliceImageQuality_FullPam));
-  }
-
-
-  
-  void OrthancSlicesLoader::ScheduleSliceImageJpeg(const Deprecated::Slice& slice,
-                                                   size_t index,
-                                                   SliceImageQuality quality)
-  {
-    unsigned int value;
-    
-    switch (quality)
-    {
-      case SliceImageQuality_Jpeg50:
-        value = 50;
-        break;
-
-      case SliceImageQuality_Jpeg90:
-        value = 90;
-        break;
-
-      case SliceImageQuality_Jpeg95:
-        value = 95;
-        break;
-      
-      default:
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-    
-    // This requires the official Web viewer plugin to be installed!
-    std::string uri = ("/web-viewer/instances/jpeg" +
-                       boost::lexical_cast<std::string>(value) +
-                       "-" + slice.GetOrthancInstanceId() + "_" +
-                       boost::lexical_cast<std::string>(slice.GetFrame()));
-
-    orthanc_.GetJsonAsync(uri,
-      new Callable<OrthancSlicesLoader, 
-        OrthancApiClient::JsonResponseReadyMessage>
-          (*this, &OrthancSlicesLoader::ParseSliceImageJpeg),
-      new Callable<OrthancSlicesLoader, 
-        IWebService::HttpRequestErrorMessage>
-          (*this, &OrthancSlicesLoader::OnSliceImageError),
-        Operation::DownloadSliceImage(
-          static_cast<unsigned int>(index), slice, quality));
-  }
-  
-  
-  
-  void OrthancSlicesLoader::ScheduleLoadSliceImage(size_t index,
-                                                   SliceImageQuality quality)
-  {
-    if (state_ != State_GeometryReady)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-    
-    const Deprecated::Slice& slice = GetSlice(index);
-    
-    if (slice.HasOrthancDecoding())
-    {
-      switch (quality)
-      {
-        case SliceImageQuality_FullPng:
-          ScheduleSliceImagePng(slice, index);
-          break;
-        case SliceImageQuality_FullPam:
-          ScheduleSliceImagePam(slice, index);
-          break;
-        default:
-          ScheduleSliceImageJpeg(slice, index, quality);
-      }
-    }
-    else
-    {
-      std::string uri = ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" +
-                         boost::lexical_cast<std::string>(slice.GetFrame()) + "/raw.gz");
-      orthanc_.GetBinaryAsync(uri, IWebService::HttpHeaders(),
-        new Callable<OrthancSlicesLoader, 
-          OrthancApiClient::BinaryResponseReadyMessage>
-            (*this, &OrthancSlicesLoader::ParseSliceRawImage),
-        new Callable<OrthancSlicesLoader,
-          IWebService::HttpRequestErrorMessage>
-            (*this, &OrthancSlicesLoader::OnSliceImageError),
-        Operation::DownloadSliceRawImage(
-          static_cast<unsigned int>(index), slice));
-    }
-  }
-}
--- a/Framework/Toolbox/OrthancSlicesLoader.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,209 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "../Messages/IObservable.h"
-#include "../StoneEnumerations.h"
-#include "IWebService.h"
-#include "OrthancApiClient.h"
-#include "SlicesSorter.h"
-#include "Slice.h"
-
-#include <Core/Images/Image.h>
-
-
-namespace OrthancStone
-{
-  class OrthancSlicesLoader : public IObservable, public IObserver
-  {
-  public:
-    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, SliceGeometryReadyMessage, OrthancSlicesLoader);
-    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, SliceGeometryErrorMessage, OrthancSlicesLoader);
-
-    
-    class SliceImageReadyMessage : public OriginMessage<OrthancSlicesLoader>
-    {
-      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
-      
-    private:
-      unsigned int                   sliceIndex_;
-      const Deprecated::Slice&                   slice_;
-      const Orthanc::ImageAccessor&  image_;
-      SliceImageQuality              effectiveQuality_;
-
-    public:
-      SliceImageReadyMessage(const OrthancSlicesLoader& origin,
-                             unsigned int sliceIndex,
-                             const Deprecated::Slice& slice,
-                             const Orthanc::ImageAccessor& image,
-                             SliceImageQuality effectiveQuality) :
-        OriginMessage(origin),
-        sliceIndex_(sliceIndex),
-        slice_(slice),
-        image_(image),
-        effectiveQuality_(effectiveQuality)
-      {
-      }
-
-      unsigned int GetSliceIndex() const
-      {
-        return sliceIndex_;
-      }
-
-      const Deprecated::Slice& GetSlice() const
-      {
-        return slice_;
-      }
-
-      const Orthanc::ImageAccessor& GetImage() const
-      {
-        return image_;
-      }
-
-      SliceImageQuality GetEffectiveQuality() const
-      {
-        return effectiveQuality_;
-      }        
-    };
-    
-
-    class SliceImageErrorMessage : public OriginMessage<OrthancSlicesLoader>
-    {
-      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
-      
-    private:
-      const Deprecated::Slice&       slice_;
-      unsigned int       sliceIndex_;
-      SliceImageQuality  effectiveQuality_;
-
-    public:
-      SliceImageErrorMessage(const OrthancSlicesLoader& origin,
-                             unsigned int sliceIndex,
-                             const Deprecated::Slice& slice,
-                             SliceImageQuality effectiveQuality) :
-        OriginMessage(origin),
-        slice_(slice),
-        sliceIndex_(sliceIndex),
-        effectiveQuality_(effectiveQuality)
-      {
-      }
-      unsigned int GetSliceIndex() const
-      {
-        return sliceIndex_;
-      }
-
-      const Deprecated::Slice& GetSlice() const
-      {
-        return slice_;
-      }
-
-      SliceImageQuality GetEffectiveQuality() const
-      {
-        return effectiveQuality_;
-      }        
-    };
-    
-  private:
-    enum State
-    {
-      State_Error,
-      State_Initialization,
-      State_LoadingGeometry,
-      State_GeometryReady
-    };
-    
-    enum Mode
-    {
-      Mode_SeriesGeometry,
-      Mode_InstanceGeometry,
-      Mode_FrameGeometry,
-      Mode_LoadImage,
-      Mode_LoadRawImage,
-      Mode_LoadDicomFile
-    };
-
-    class Operation;
-
-    OrthancApiClient&  orthanc_;
-    State         state_;
-    SlicesSorter  slices_;
-
-    void NotifySliceImageSuccess(const Operation& operation,
-                                 const Orthanc::ImageAccessor& image);
-    
-    void NotifySliceImageError(const Operation& operation);
-
-    void OnGeometryError(const IWebService::HttpRequestErrorMessage& message);
-
-    void OnSliceImageError(const IWebService::HttpRequestErrorMessage& message);
-
-    void ParseSeriesGeometry(const OrthancApiClient::JsonResponseReadyMessage& message);
-
-    void ParseInstanceGeometry(const OrthancApiClient::JsonResponseReadyMessage& message);
-
-    void ParseFrameGeometry(const OrthancApiClient::JsonResponseReadyMessage& message);
-
-    void ParseSliceImagePng(const OrthancApiClient::BinaryResponseReadyMessage& message);
-
-    void ParseSliceImagePam(const OrthancApiClient::BinaryResponseReadyMessage& message);
-
-    void ParseSliceImageJpeg(const OrthancApiClient::JsonResponseReadyMessage& message);
-
-    void ParseSliceRawImage(const OrthancApiClient::BinaryResponseReadyMessage& message);
-
-    void ScheduleSliceImagePng(const Deprecated::Slice& slice,
-                               size_t index);
-
-    void ScheduleSliceImagePam(const Deprecated::Slice& slice,
-                               size_t index);
-
-    void ScheduleSliceImageJpeg(const Deprecated::Slice& slice,
-                                size_t index,
-                                SliceImageQuality quality);
-
-    void SortAndFinalizeSlices();
-    
-  public:
-    OrthancSlicesLoader(MessageBroker& broker,
-                        //ISliceLoaderObserver& callback,
-                        OrthancApiClient& orthancApi);
-
-    void ScheduleLoadSeries(const std::string& seriesId);
-
-    void ScheduleLoadInstance(const std::string& instanceId);
-
-    void ScheduleLoadFrame(const std::string& instanceId,
-                           unsigned int frame);
-
-    bool IsGeometryReady() const;
-
-    size_t GetSlicesCount() const;
-
-    const Deprecated::Slice& GetSlice(size_t index) const;
-
-    bool LookupSlice(size_t& index,
-                     const CoordinateSystem3D& plane) const;
-
-    void ScheduleLoadSliceImage(size_t index,
-                                SliceImageQuality requestedQuality);
-  };
-}
--- a/Framework/Toolbox/ParallelSlices.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Toolbox/ParallelSlices.cpp	Wed May 22 16:13:46 2019 +0200
@@ -22,6 +22,7 @@
 #include "ParallelSlices.h"
 
 #include "GeometryToolbox.h"
+#include "../Volumes/ImageBuffer3D.h"
 
 #include <Core/Logging.h>
 #include <Core/OrthancException.h>
@@ -30,7 +31,7 @@
 {
   ParallelSlices::ParallelSlices()
   {
-    LinearAlgebra::AssignVector(normal_, 0, 0, 1);
+    Clear();
   }
 
 
@@ -48,7 +49,7 @@
   }
 
 
-  ParallelSlices::~ParallelSlices()
+  void ParallelSlices::Clear()
   {
     for (size_t i = 0; i < slices_.size(); i++)
     {
@@ -58,6 +59,15 @@
         slices_[i] = NULL;
       }
     }
+
+    slices_.clear();
+    LinearAlgebra::AssignVector(normal_, 0, 0, 1);
+  }
+  
+
+  ParallelSlices::~ParallelSlices()
+  {
+    Clear();
   }
 
 
@@ -146,4 +156,60 @@
 
     return reversed.release();
   }
+
+
+  ParallelSlices* ParallelSlices::FromVolumeImage(const VolumeImageGeometry& geometry,
+                                                  VolumeProjection projection)
+  {
+    const Vector dimensions = geometry.GetVoxelDimensions(VolumeProjection_Axial);
+    const CoordinateSystem3D& axial = geometry.GetAxialGeometry();
+    
+    std::auto_ptr<ParallelSlices> result(new ParallelSlices);
+
+    switch (projection)
+    {
+      case VolumeProjection_Axial:
+        for (unsigned int z = 0; z < geometry.GetDepth(); z++)
+        {
+          Vector origin = axial.GetOrigin();
+          origin += static_cast<double>(z) * dimensions[2] * axial.GetNormal();
+
+          result->AddSlice(origin,
+                           axial.GetAxisX(),
+                           axial.GetAxisY());
+        }
+        break;
+
+      case VolumeProjection_Coronal:
+        for (unsigned int y = 0; y < geometry.GetHeight(); y++)
+        {
+          Vector origin = axial.GetOrigin();
+          origin += static_cast<double>(y) * dimensions[1] * axial.GetAxisY();
+          origin += static_cast<double>(geometry.GetDepth() - 1) * dimensions[2] * axial.GetNormal();
+
+          result->AddSlice(origin,
+                           axial.GetAxisX(),
+                           -axial.GetNormal());
+        }
+        break;
+
+      case VolumeProjection_Sagittal:
+        for (unsigned int x = 0; x < geometry.GetWidth(); x++)
+        {
+          Vector origin = axial.GetOrigin();
+          origin += static_cast<double>(x) * dimensions[0] * axial.GetAxisX();
+          origin += static_cast<double>(geometry.GetDepth() - 1) * dimensions[2] * axial.GetNormal();
+
+          result->AddSlice(origin,
+                           axial.GetAxisY(),
+                           -axial.GetNormal());
+        }
+        break;
+
+      default:
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+
+    return result.release();
+  }
 }
--- a/Framework/Toolbox/ParallelSlices.h	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Toolbox/ParallelSlices.h	Wed May 22 16:13:46 2019 +0200
@@ -22,6 +22,7 @@
 #pragma once
 
 #include "CoordinateSystem3D.h"
+#include "../Volumes/VolumeImageGeometry.h"
 
 namespace OrthancStone
 {
@@ -33,6 +34,8 @@
     
     ParallelSlices& operator= (const ParallelSlices& other);  // Forbidden
 
+    void Clear();
+
   public:
     ParallelSlices();
 
@@ -63,5 +66,8 @@
                              const Vector& origin) const;
 
     ParallelSlices* Reverse() const;
+
+    static ParallelSlices* FromVolumeImage(const VolumeImageGeometry& geometry,
+                                           VolumeProjection projection);
   };
 }
--- a/Framework/Toolbox/ShearWarpProjectiveTransform.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Toolbox/ShearWarpProjectiveTransform.cpp	Wed May 22 16:13:46 2019 +0200
@@ -343,6 +343,7 @@
                                  float& maxValue,
                                  const Matrix& M_view,
                                  const ImageBuffer3D& source,
+                                 const VolumeImageGeometry& geometry,
                                  double pixelSpacing,
                                  unsigned int countSlices,
                                  ImageInterpolation shearInterpolation,
@@ -385,8 +386,8 @@
     
     // Compute the "world" matrix that maps the source volume to the
     // (0,0,0)->(1,1,1) unit cube
-    Vector origin = source.GetGeometry().GetCoordinates(0, 0, 0);
-    Vector ps = source.GetGeometry().GetVoxelDimensions(VolumeProjection_Axial);
+    Vector origin = geometry.GetCoordinates(0, 0, 0);
+    Vector ps = geometry.GetVoxelDimensions(VolumeProjection_Axial);
     Matrix world = LinearAlgebra::Product(
       GeometryToolbox::CreateScalingMatrix(1.0 / ps[0], 1.0 / ps[1], 1.0 / ps[2]),
       GeometryToolbox::CreateTranslationMatrix(-origin[0], -origin[1], -origin[2]));
@@ -598,6 +599,7 @@
                                   float& maxValue,
                                   const Matrix& M_view,
                                   const ImageBuffer3D& source,
+                                  const VolumeImageGeometry& geometry,
                                   bool mip,
                                   double pixelSpacing,
                                   unsigned int countSlices,
@@ -607,13 +609,13 @@
     if (mip)
     {
       ApplyAxialInternal<SourceFormat, TargetFormat, true>
-        (target, maxValue, M_view, source, pixelSpacing,
+        (target, maxValue, M_view, source, geometry, pixelSpacing,
          countSlices, shearInterpolation, warpInterpolation);
     }
     else
     {
       ApplyAxialInternal<SourceFormat, TargetFormat, false>
-        (target, maxValue, M_view, source, pixelSpacing,
+        (target, maxValue, M_view, source, geometry, pixelSpacing,
          countSlices, shearInterpolation, warpInterpolation);
     } 
   }
@@ -623,6 +625,7 @@
   ShearWarpProjectiveTransform::ApplyAxial(float& maxValue,
                                            const Matrix& M_view,
                                            const ImageBuffer3D& source,
+                                           const VolumeImageGeometry& geometry,
                                            Orthanc::PixelFormat targetFormat,
                                            unsigned int targetWidth,
                                            unsigned int targetHeight,
@@ -640,7 +643,7 @@
     {
       ApplyAxialInternal2<Orthanc::PixelFormat_Grayscale16,
                           Orthanc::PixelFormat_Grayscale16>
-        (*target, maxValue, M_view, source, mip, pixelSpacing,
+        (*target, maxValue, M_view, source, geometry, mip, pixelSpacing,
          countSlices, shearInterpolation, warpInterpolation);
     }
     else if (source.GetFormat() == Orthanc::PixelFormat_SignedGrayscale16 &&
@@ -648,7 +651,7 @@
     {
       ApplyAxialInternal2<Orthanc::PixelFormat_SignedGrayscale16,
                           Orthanc::PixelFormat_SignedGrayscale16>
-        (*target, maxValue, M_view, source, mip, pixelSpacing,
+        (*target, maxValue, M_view, source, geometry, mip, pixelSpacing,
          countSlices, shearInterpolation, warpInterpolation);
     }
     else
--- a/Framework/Toolbox/ShearWarpProjectiveTransform.h	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Toolbox/ShearWarpProjectiveTransform.h	Wed May 22 16:13:46 2019 +0200
@@ -92,6 +92,7 @@
     static Orthanc::ImageAccessor* ApplyAxial(float& maxValue,
                                               const Matrix& M_view,  // cf. "CalibrateView()"
                                               const ImageBuffer3D& source,
+                                              const VolumeImageGeometry& geometry,
                                               Orthanc::PixelFormat targetFormat,
                                               unsigned int targetWidth,
                                               unsigned int targetHeight,
--- a/Framework/Toolbox/Slice.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,367 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "Slice.h"
-
-#include "../StoneEnumerations.h"
-#include "GeometryToolbox.h"
-
-#include <Core/Logging.h>
-#include <Core/OrthancException.h>
-#include <Core/Toolbox.h>
-
-#include <boost/lexical_cast.hpp>
-
-namespace Deprecated
-{
-  static bool ParseDouble(double& target,
-                          const std::string& source)
-  {
-    try
-    {
-      target = boost::lexical_cast<double>(source);
-      return true;
-    }
-    catch (boost::bad_lexical_cast&)
-    {
-      return false;
-    }
-  }
-
-  Slice* Slice::Clone() const
-  {
-    std::auto_ptr<Slice> target(new Slice());
-
-    target->type_ = type_;
-    target->orthancInstanceId_ = orthancInstanceId_;
-    target->sopClassUid_ = sopClassUid_;
-    target->frame_ = frame_;
-    target->frameCount_ = frameCount_;
-    target->geometry_ = geometry_;
-    target->pixelSpacingX_ = pixelSpacingX_;
-    target->pixelSpacingY_ = pixelSpacingY_;
-    target->thickness_ = thickness_;
-    target->width_ = width_;
-    target->height_ = height_;
-    target->converter_ = converter_;
-    if (imageInformation_.get() != NULL)
-      target->imageInformation_.reset(imageInformation_->Clone());
-
-    return target.release();
-  }
-  
-  bool Slice::ComputeRTDoseGeometry(const Orthanc::DicomMap& dataset,
-                                    unsigned int frame)
-  {
-    // http://dicom.nema.org/medical/Dicom/2016a/output/chtml/part03/sect_C.8.8.3.2.html
-
-    {
-      std::string increment;
-
-      if (dataset.CopyToString(increment, Orthanc::DICOM_TAG_FRAME_INCREMENT_POINTER, false))
-      {
-        Orthanc::Toolbox::ToUpperCase(increment);
-        if (increment != "3004,000C")  // This is the "Grid Frame Offset Vector" tag
-        {
-          LOG(ERROR) << "Bad value for the \"FrameIncrementPointer\" tag";
-          return false;
-        }
-      }
-    }
-
-    std::string offsetTag;
-
-    if (!dataset.CopyToString(offsetTag, Orthanc::DICOM_TAG_GRID_FRAME_OFFSET_VECTOR, false) ||
-        offsetTag.empty())
-    {
-      LOG(ERROR) << "Cannot read the \"GridFrameOffsetVector\" tag, check you are using Orthanc >= 1.3.1";
-      return false;
-    }
-
-    std::vector<std::string> offsets;
-    Orthanc::Toolbox::TokenizeString(offsets, offsetTag, '\\');
-
-    if (frameCount_ <= 1 ||
-        offsets.size() < frameCount_ ||
-        offsets.size() < 2 ||
-        frame >= frameCount_)
-    {
-      LOG(ERROR) << "No information about the 3D location of some slice(s) in a RT DOSE";
-      return false;
-    }
-
-    double offset0, offset1, z;
-
-    if (!ParseDouble(offset0, offsets[0]) ||
-        !ParseDouble(offset1, offsets[1]) ||
-        !ParseDouble(z, offsets[frame]))
-    {
-      LOG(ERROR) << "Invalid syntax";
-      return false;
-    }
-
-    if (!OrthancStone::LinearAlgebra::IsCloseToZero(offset0))
-    {
-      LOG(ERROR) << "Invalid syntax";
-      return false;
-    }
-
-    geometry_ = OrthancStone::CoordinateSystem3D(geometry_.GetOrigin() + z * geometry_.GetNormal(),
-                                                 //+ 650 * geometry_.GetAxisX(),
-                                                 geometry_.GetAxisX(),
-                                                 geometry_.GetAxisY());
-
-    thickness_ = offset1 - offset0;
-    if (thickness_ < 0)
-    {
-      thickness_ = -thickness_;
-    }
-
-    return true;
-  }
-
-  
-  bool Slice::ParseOrthancFrame(const Orthanc::DicomMap& dataset,
-                                const std::string& instanceId,
-                                unsigned int frame)
-  {
-    orthancInstanceId_ = instanceId;
-    frame_ = frame;
-    type_ = Type_OrthancDecodableFrame;
-    imageInformation_.reset(new Orthanc::DicomImageInformation(dataset));
-
-    if (!dataset.CopyToString(sopClassUid_, Orthanc::DICOM_TAG_SOP_CLASS_UID, false) ||
-        sopClassUid_.empty())
-    {
-      LOG(ERROR) << "Instance without a SOP class UID";
-      return false; 
-    }
-
-    if (!dataset.ParseUnsignedInteger32(frameCount_, Orthanc::DICOM_TAG_NUMBER_OF_FRAMES))
-    {
-      frameCount_ = 1;   // Assume instance with one frame
-    }
-
-    if (frame >= frameCount_)
-    {
-      return false;
-    }
-
-    if (!dataset.ParseUnsignedInteger32(width_, Orthanc::DICOM_TAG_COLUMNS) ||
-        !dataset.ParseUnsignedInteger32(height_, Orthanc::DICOM_TAG_ROWS))
-    {
-      return false;
-    }
-
-    thickness_ = 100.0 * std::numeric_limits<double>::epsilon();
-
-    std::string tmp;
-    if (dataset.CopyToString(tmp, Orthanc::DICOM_TAG_SLICE_THICKNESS, false))
-    {
-      if (!tmp.empty() &&
-          !ParseDouble(thickness_, tmp))
-      {
-        return false;  // Syntax error
-      }
-    }
-    
-    converter_.ReadParameters(dataset);
-
-    OrthancStone::GeometryToolbox::GetPixelSpacing(pixelSpacingX_, pixelSpacingY_, dataset);
-
-    std::string position, orientation;
-    if (dataset.CopyToString(position, Orthanc::DICOM_TAG_IMAGE_POSITION_PATIENT, false) &&
-        dataset.CopyToString(orientation, Orthanc::DICOM_TAG_IMAGE_ORIENTATION_PATIENT, false))
-    {
-      geometry_ = OrthancStone::CoordinateSystem3D(position, orientation);
-
-      bool ok = true;
-
-      switch (OrthancStone::StringToSopClassUid(sopClassUid_))
-      {
-        case OrthancStone::SopClassUid_RTDose:
-          type_ = Type_OrthancRawFrame;
-          ok = ComputeRTDoseGeometry(dataset, frame);
-          break;
-            
-        default:
-          break;
-      }
-
-      if (!ok)
-      {
-        LOG(ERROR) << "Cannot deduce the 3D location of frame " << frame
-                   << " in instance " << instanceId << ", whose SOP class UID is: " << sopClassUid_;
-        return false;
-      }
-    }
-
-    return true;
-  }
-
-  
-  const std::string Slice::GetOrthancInstanceId() const
-  {
-    if (type_ == Type_OrthancDecodableFrame ||
-        type_ == Type_OrthancRawFrame)
-    {
-      return orthancInstanceId_;
-    }
-    else
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }   
-  }
-
-  
-  unsigned int Slice::GetFrame() const
-  {
-    if (type_ == Type_Invalid)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-        
-    return frame_;
-  }
-
-  
-  const OrthancStone::CoordinateSystem3D& Slice::GetGeometry() const
-  {
-    if (type_ == Type_Invalid)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-        
-    return geometry_;
-  }
-
-  
-  double Slice::GetThickness() const
-  {
-    if (type_ == Type_Invalid)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-        
-    return thickness_;
-  }
-
-  
-  double Slice::GetPixelSpacingX() const
-  {
-    if (type_ == Type_Invalid)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-        
-    return pixelSpacingX_;
-  }
-
-  
-  double Slice::GetPixelSpacingY() const
-  {
-    if (type_ == Type_Invalid)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-        
-    return pixelSpacingY_;
-  }
-
-  
-  unsigned int Slice::GetWidth() const
-  {
-    if (type_ == Type_Invalid)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-        
-    return width_;
-  }
-
-  
-  unsigned int Slice::GetHeight() const
-  {
-    if (type_ == Type_Invalid)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-        
-    return height_;
-  }
-
-
-  const DicomFrameConverter& Slice::GetConverter() const
-  {
-    if (type_ == Type_Invalid)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-        
-    return converter_;
-  }
-
-
-  bool Slice::ContainsPlane(const OrthancStone::CoordinateSystem3D& plane) const
-  {
-    if (type_ == Type_Invalid)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-
-    bool opposite;
-    return (OrthancStone::GeometryToolbox::IsParallelOrOpposite(opposite,
-                                                                GetGeometry().GetNormal(),
-                                                                plane.GetNormal()) &&
-            OrthancStone::LinearAlgebra::IsNear(GetGeometry().ProjectAlongNormal(GetGeometry().GetOrigin()),
-                                                GetGeometry().ProjectAlongNormal(plane.GetOrigin()),
-                                                thickness_ / 2.0));
-  }
-
-  
-  void Slice::GetExtent(std::vector<OrthancStone::Vector>& points) const
-  {
-    double sx = GetPixelSpacingX();
-    double sy = GetPixelSpacingY();
-    double w = static_cast<double>(GetWidth());
-    double h = static_cast<double>(GetHeight());
-
-    points.clear();
-    points.push_back(GetGeometry().MapSliceToWorldCoordinates(-0.5      * sx, -0.5      * sy));
-    points.push_back(GetGeometry().MapSliceToWorldCoordinates((w - 0.5) * sx, -0.5      * sy));
-    points.push_back(GetGeometry().MapSliceToWorldCoordinates(-0.5      * sx, (h - 0.5) * sy));
-    points.push_back(GetGeometry().MapSliceToWorldCoordinates((w - 0.5) * sx, (h - 0.5) * sy));
-  }
-
-
-  const Orthanc::DicomImageInformation& Slice::GetImageInformation() const
-  {
-    if (imageInformation_.get() == NULL)
-    {
-      // Only available if constructing the "Slice" object with a DICOM map
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-    else
-    {
-      return *imageInformation_;
-    }
-  }
-}
--- a/Framework/Toolbox/Slice.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,155 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "CoordinateSystem3D.h"
-#include "DicomFrameConverter.h"
-
-#include <Core/DicomFormat/DicomImageInformation.h>
-#include <Core/IDynamicObject.h>
-
-namespace Deprecated
-{
-  // TODO - Remove this class
-  class Slice :
-    public Orthanc::IDynamicObject  /* to be used as a payload of SlicesSorter */
-  {
-  private:
-    enum Type
-    {
-      Type_Invalid,
-      Type_Standalone,
-      Type_OrthancDecodableFrame,
-      Type_OrthancRawFrame
-      // TODO A slice could come from some DICOM file (URL)
-    };
-
-    bool ComputeRTDoseGeometry(const Orthanc::DicomMap& dataset,
-                               unsigned int frame);
-
-    Type                 type_;
-    std::string          orthancInstanceId_;
-    std::string          sopClassUid_;
-    unsigned int         frame_;
-    unsigned int         frameCount_;   // TODO : Redundant with "imageInformation_"
-    OrthancStone::CoordinateSystem3D   geometry_;
-    double               pixelSpacingX_;
-    double               pixelSpacingY_;
-    double               thickness_;
-    unsigned int         width_;   // TODO : Redundant with "imageInformation_"
-    unsigned int         height_;   // TODO : Redundant with "imageInformation_"
-    DicomFrameConverter  converter_;   // TODO : Partially redundant with "imageInformation_"
-
-    std::auto_ptr<Orthanc::DicomImageInformation>  imageInformation_;
-
-  public:
-    Slice() :
-      type_(Type_Invalid)
-    {
-    }
-
-
-    // this constructor is used to reference, i.e, a slice that is being loaded
-    Slice(const std::string& orthancInstanceId,
-          unsigned int frame) :
-      type_(Type_Invalid),
-      orthancInstanceId_(orthancInstanceId),
-      frame_(frame)
-    {        
-    }
-
-    // TODO Is this constructor the best way to go to tackle missing
-    // layers within SliceViewerWidget?
-    Slice(const OrthancStone::CoordinateSystem3D& plane,
-          double thickness) :
-      type_(Type_Standalone),
-      frame_(0),
-      frameCount_(0),
-      geometry_(plane),
-      pixelSpacingX_(1),
-      pixelSpacingY_(1),
-      thickness_(thickness),
-      width_(0),
-      height_(0)
-    {      
-    }
-
-    Slice(const OrthancStone::CoordinateSystem3D& plane,
-          double pixelSpacingX,
-          double pixelSpacingY,
-          double thickness,
-          unsigned int width,
-          unsigned int height,
-          const DicomFrameConverter& converter) :
-      type_(Type_Standalone),
-      frameCount_(1),
-      geometry_(plane),
-      pixelSpacingX_(pixelSpacingX),
-      pixelSpacingY_(pixelSpacingY),
-      thickness_(thickness),
-      width_(width),
-      height_(height),
-      converter_(converter)
-    {
-    }
-
-    bool IsValid() const
-    {
-      return type_ != Type_Invalid;
-    } 
-
-    bool ParseOrthancFrame(const Orthanc::DicomMap& dataset,
-                           const std::string& instanceId,
-                           unsigned int frame);
-
-    bool HasOrthancDecoding() const
-    {
-      return type_ == Type_OrthancDecodableFrame;
-    }
-
-    const std::string GetOrthancInstanceId() const;
-
-    unsigned int GetFrame() const;
-
-    const OrthancStone::CoordinateSystem3D& GetGeometry() const;
-
-    double GetThickness() const;
-
-    double GetPixelSpacingX() const;
-
-    double GetPixelSpacingY() const;
-
-    unsigned int GetWidth() const;
-
-    unsigned int GetHeight() const;
-
-    const DicomFrameConverter& GetConverter() const;
-
-    bool ContainsPlane(const OrthancStone::CoordinateSystem3D& plane) const;
-
-    void GetExtent(std::vector<OrthancStone::Vector>& points) const;
-
-    const Orthanc::DicomImageInformation& GetImageInformation() const;
-
-    Slice* Clone() const;
-  };
-}
--- a/Framework/Toolbox/ViewportGeometry.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,217 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "ViewportGeometry.h"
-
-#include <Core/Logging.h>
-#include <Core/OrthancException.h>
-
-#include <boost/math/special_functions/round.hpp>
-
-namespace OrthancStone
-{
-  void ViewportGeometry::ComputeTransform()
-  {
-    // The following lines must be read in reverse order!
-    cairo_matrix_t tmp;
-    
-    // Bring the center of the scene to the center of the view
-    cairo_matrix_init_translate(&transform_, 
-                                panX_ + static_cast<double>(width_) / 2.0, 
-                                panY_ + static_cast<double>(height_) / 2.0);
-
-    // Apply the zoom around (0,0)
-    cairo_matrix_init_scale(&tmp, zoom_, zoom_);
-    cairo_matrix_multiply(&transform_, &tmp, &transform_);
-
-    // Bring the center of the scene to (0,0)
-    cairo_matrix_init_translate(&tmp,
-                                -(sceneExtent_.GetX1() + sceneExtent_.GetX2()) / 2.0,
-                                -(sceneExtent_.GetY1() + sceneExtent_.GetY2()) / 2.0);
-    cairo_matrix_multiply(&transform_, &tmp, &transform_);
-  }
-
-
-  ViewportGeometry::ViewportGeometry()
-  {
-    width_ = 0;
-    height_ = 0;
-
-    zoom_ = 1;
-    panX_ = 0;
-    panY_ = 0;
-
-    ComputeTransform();
-  }
-
-
-  void ViewportGeometry::SetDisplaySize(unsigned int width,
-                                        unsigned int height)
-  {
-    if (width_ != width ||
-        height_ != height)
-    {
-      LOG(INFO) << "New display size: " << width << "x" << height;
-
-      width_ = width;
-      height_ = height;
-
-      ComputeTransform();
-    }
-  }
-
-
-  void ViewportGeometry::SetSceneExtent(const Extent2D& extent)
-  {
-    LOG(INFO) << "New scene extent: ("
-              << extent.GetX1() << "," << extent.GetY1() << ") => ("
-              << extent.GetX2() << "," << extent.GetY2() << ")";
-
-    sceneExtent_ = extent;
-    ComputeTransform();
-  }
-
-
-  void ViewportGeometry::MapDisplayToScene(double& sceneX /* out */,
-                                           double& sceneY /* out */,
-                                           double x,
-                                           double y) const
-  {
-    cairo_matrix_t transform = transform_;
-
-    if (cairo_matrix_invert(&transform) != CAIRO_STATUS_SUCCESS)
-    {
-      LOG(ERROR) << "Cannot invert singular matrix";
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-    }
-
-    sceneX = x;
-    sceneY = y;
-    cairo_matrix_transform_point(&transform, &sceneX, &sceneY);
-  }
-
-
-  void ViewportGeometry::MapSceneToDisplay(int& displayX /* out */,
-                                           int& displayY /* out */,
-                                           double x,
-                                           double y) const
-  {
-    cairo_matrix_transform_point(&transform_, &x, &y);
-
-    displayX = static_cast<int>(boost::math::iround(x));
-    displayY = static_cast<int>(boost::math::iround(y));
-  }
-
-
-  void ViewportGeometry::MapPixelCenterToScene(std::vector<Touch>& sceneTouches /* out */,
-                                               const std::vector<Touch>& displayTouches) const
-  {
-    double sceneX, sceneY;
-    sceneTouches.clear();
-    for (size_t t = 0; t < displayTouches.size(); t++)
-    {
-      MapPixelCenterToScene(
-        sceneX,
-        sceneY, 
-        static_cast<int>(displayTouches[t].x), 
-        static_cast<int>(displayTouches[t].y));
-      
-      sceneTouches.push_back(Touch((float)sceneX, (float)sceneY));
-    }
-  }
-
-  void ViewportGeometry::MapPixelCenterToScene(double& sceneX,
-                                               double& sceneY,
-                                               int x,
-                                               int y) const
-  {
-    // Take the center of the pixel
-    MapDisplayToScene(sceneX, sceneY,
-                      static_cast<double>(x) + 0.5,
-                      static_cast<double>(y) + 0.5);
-  }
-
-
-  void ViewportGeometry::FitContent()
-  {
-    if (width_ > 0 &&
-        height_ > 0 &&
-        !sceneExtent_.IsEmpty())
-    {
-      double zoomX = static_cast<double>(width_) / (sceneExtent_.GetX2() - sceneExtent_.GetX1());
-      double zoomY = static_cast<double>(height_) / (sceneExtent_.GetY2() - sceneExtent_.GetY1());
-      zoom_ = zoomX < zoomY ? zoomX : zoomY;
-
-      panX_ = 0;
-      panY_ = 0;
-
-      ComputeTransform();
-    }
-  }
-
-
-  void ViewportGeometry::ApplyTransform(CairoContext& context) const
-  {
-    cairo_set_matrix(context.GetObject(), &transform_);
-  }
-
-
-  void ViewportGeometry::GetPan(double& x,
-                                double& y) const
-  {
-    x = panX_;
-    y = panY_;
-  }
-
-
-  void ViewportGeometry::SetPan(double x,
-                                double y)
-  {
-    panX_ = x;
-    panY_ = y;
-    ComputeTransform();
-  }
-
-
-  void ViewportGeometry::SetZoom(double zoom)
-  {
-    zoom_ = zoom;
-    ComputeTransform();
-  }
-
-
-  Matrix ViewportGeometry::GetMatrix() const
-  {
-    Matrix m(3, 3);
-
-    m(0, 0) = transform_.xx;
-    m(0, 1) = transform_.xy;
-    m(0, 2) = transform_.x0;
-    m(1, 0) = transform_.yx;
-    m(1, 1) = transform_.yy;
-    m(1, 2) = transform_.y0;
-    m(2, 0) = 0;
-    m(2, 1) = 0;
-    m(2, 2) = 1;
-    
-    return m;
-  }
-}
--- a/Framework/Toolbox/ViewportGeometry.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,110 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "../Viewport/CairoContext.h"
-#include "Extent2D.h"
-#include "LinearAlgebra.h"
-#include "../Viewport/IMouseTracker.h"  // to include "Touch" definition
-
-namespace OrthancStone
-{
-  class ViewportGeometry
-  {
-  private:
-    // Extent of the scene (in world units)
-    Extent2D   sceneExtent_;
-
-    // Size of the display (in pixels)
-    unsigned int  width_;
-    unsigned int  height_;
-
-    // Zoom/pan
-    double   zoom_;
-    double   panX_;  // In pixels (display units)
-    double   panY_;
-
-    cairo_matrix_t  transform_;  // Scene-to-display transformation
-
-    void ComputeTransform();
-
-  public:
-    ViewportGeometry();
-
-    void SetDisplaySize(unsigned int width,
-                        unsigned int height);
-
-    void SetSceneExtent(const Extent2D& extent);
-
-    const Extent2D& GetSceneExtent() const
-    {
-      return sceneExtent_;
-    }
-
-    void MapDisplayToScene(double& sceneX /* out */,
-                           double& sceneY /* out */,
-                           double x,
-                           double y) const;
-
-    void MapPixelCenterToScene(double& sceneX /* out */,
-                               double& sceneY /* out */,
-                               int x,
-                               int y) const;
-
-    void MapPixelCenterToScene(std::vector<Touch>& sceneTouches /* out */,
-                               const std::vector<Touch>& displayTouches) const;
-
-    void MapSceneToDisplay(int& displayX /* out */,
-                           int& displayY /* out */,
-                           double x,
-                           double y) const;
-
-    unsigned int GetDisplayWidth() const
-    {
-      return width_;
-    }
-
-    unsigned int GetDisplayHeight() const
-    {
-      return height_;
-    }
-
-    double GetZoom() const
-    {
-      return zoom_;
-    }
-
-    void FitContent();
-
-    void ApplyTransform(CairoContext& context) const;
-
-    void GetPan(double& x,
-                double& y) const;
-
-    void SetPan(double x,
-                double y);
-
-    void SetZoom(double zoom);
-
-    Matrix GetMatrix() const;
-  };
-}
--- a/Framework/Toolbox/VolumeImageGeometry.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,309 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "VolumeImageGeometry.h"
-
-#include "../Toolbox/GeometryToolbox.h"
-
-#include <Core/OrthancException.h>
-
-
-namespace OrthancStone
-{
-  void VolumeImageGeometry::Invalidate()
-  {
-    Vector p = (axialGeometry_.GetOrigin() +
-                static_cast<double>(depth_ - 1) * voxelDimensions_[2] * axialGeometry_.GetNormal());
-        
-    coronalGeometry_ = CoordinateSystem3D(p,
-                                          axialGeometry_.GetAxisX(),
-                                          -axialGeometry_.GetNormal());
-    
-    sagittalGeometry_ = CoordinateSystem3D(p,
-                                           axialGeometry_.GetAxisY(),
-                                           axialGeometry_.GetNormal());
-
-    Vector origin = (
-      axialGeometry_.MapSliceToWorldCoordinates(-0.5 * voxelDimensions_[0],
-                                                -0.5 * voxelDimensions_[1]) -
-      0.5 * voxelDimensions_[2] * axialGeometry_.GetNormal());
-
-    Vector scaling;
-    
-    if (width_ == 0 ||
-        height_ == 0 ||
-        depth_ == 0)
-    {
-      LinearAlgebra::AssignVector(scaling, 1, 1, 1);
-    }
-    else
-    {
-      scaling = (
-        axialGeometry_.GetAxisX() * voxelDimensions_[0] * static_cast<double>(width_) +
-        axialGeometry_.GetAxisY() * voxelDimensions_[1] * static_cast<double>(height_) +
-        axialGeometry_.GetNormal() * voxelDimensions_[2] * static_cast<double>(depth_));
-    }
-
-    transform_ = LinearAlgebra::Product(
-      GeometryToolbox::CreateTranslationMatrix(origin[0], origin[1], origin[2]),
-      GeometryToolbox::CreateScalingMatrix(scaling[0], scaling[1], scaling[2]));
-
-    LinearAlgebra::InvertMatrix(transformInverse_, transform_);
-  }
-
-  
-  VolumeImageGeometry::VolumeImageGeometry() :
-    width_(0),
-    height_(0),
-    depth_(0)
-  {
-    LinearAlgebra::AssignVector(voxelDimensions_, 1, 1, 1);
-    Invalidate();
-  }
-
-
-  void VolumeImageGeometry::SetSize(unsigned int width,
-                                    unsigned int height,
-                                    unsigned int depth)
-  {
-    width_ = width;
-    height_ = height;
-    depth_ = depth;
-    Invalidate();
-  }
-
-  
-  void VolumeImageGeometry::SetAxialGeometry(const CoordinateSystem3D& geometry)
-  {
-    axialGeometry_ = geometry;
-    Invalidate();
-  }
-
-
-  void VolumeImageGeometry::SetVoxelDimensions(double x,
-                                               double y,
-                                               double z)
-  {
-    if (x <= 0 ||
-        y <= 0 ||
-        z <= 0)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-    else
-    {
-      LinearAlgebra::AssignVector(voxelDimensions_, x, y, z);
-      Invalidate();
-    }
-  }
-
-
-  const CoordinateSystem3D& VolumeImageGeometry::GetProjectionGeometry(VolumeProjection projection) const
-  {
-    switch (projection)
-    {
-      case VolumeProjection_Axial:
-        return axialGeometry_;
-
-      case VolumeProjection_Coronal:
-        return coronalGeometry_;
-
-      case VolumeProjection_Sagittal:
-        return sagittalGeometry_;
-
-      default:
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-  }
-
-  
-  Vector VolumeImageGeometry::GetVoxelDimensions(VolumeProjection projection) const
-  {
-    switch (projection)
-    {
-      case VolumeProjection_Axial:
-        return voxelDimensions_;
-
-      case VolumeProjection_Coronal:
-        return LinearAlgebra::CreateVector(voxelDimensions_[0], voxelDimensions_[2], voxelDimensions_[1]);
-
-      case VolumeProjection_Sagittal:
-        return LinearAlgebra::CreateVector(voxelDimensions_[1], voxelDimensions_[2], voxelDimensions_[0]);
-
-      default:
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-  }
-
-
-  unsigned int VolumeImageGeometry::GetProjectionWidth(VolumeProjection projection) const
-  {
-    switch (projection)
-    {
-      case VolumeProjection_Axial:
-        return width_;
-
-      case VolumeProjection_Coronal:
-        return width_;
-
-      case VolumeProjection_Sagittal:
-        return height_;
-
-      default:
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-  }
-
-
-  unsigned int VolumeImageGeometry::GetProjectionHeight(VolumeProjection projection) const
-  {
-    switch (projection)
-    {
-      case VolumeProjection_Axial:
-        return height_;
-
-      case VolumeProjection_Coronal:
-        return depth_;
-
-      case VolumeProjection_Sagittal:
-        return depth_;
-
-      default:
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-  }
-
-
-  unsigned int VolumeImageGeometry::GetProjectionDepth(VolumeProjection projection) const
-  {
-    switch (projection)
-    {
-      case VolumeProjection_Axial:
-        return depth_;
-
-      case VolumeProjection_Coronal:
-        return height_;
-
-      case VolumeProjection_Sagittal:
-        return width_;
-
-      default:
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }    
-  }
-
-
-  Vector VolumeImageGeometry::GetCoordinates(float x,
-                                             float y,
-                                             float z) const
-  {
-    Vector p = LinearAlgebra::Product(transform_, LinearAlgebra::CreateVector(x, y, z, 1));
-
-    assert(LinearAlgebra::IsNear(p[3], 1));  // Affine transform, no perspective effect
-
-    // Back to non-homogeneous coordinates
-    return LinearAlgebra::CreateVector(p[0], p[1], p[2]);
-  }
-
-
-  bool VolumeImageGeometry::DetectProjection(VolumeProjection& projection,
-                                             const Vector& planeNormal) const
-  {
-    if (GeometryToolbox::IsParallel(planeNormal, axialGeometry_.GetNormal()))
-    {
-      projection = VolumeProjection_Axial;
-      return true;
-    }
-    else if (GeometryToolbox::IsParallel(planeNormal, coronalGeometry_.GetNormal()))
-    {
-      projection = VolumeProjection_Coronal;
-      return true;
-    }
-    else if (GeometryToolbox::IsParallel(planeNormal, sagittalGeometry_.GetNormal()))
-    {
-      projection = VolumeProjection_Sagittal;
-      return true;
-    }
-    else
-    {
-      return false;
-    }
-  }
-
-  
-  bool VolumeImageGeometry::DetectSlice(VolumeProjection& projection,
-                                        unsigned int& slice,
-                                        const CoordinateSystem3D& plane) const
-  {
-    if (!DetectProjection(projection, plane.GetNormal()))
-    {
-      return false;
-    }
-
-    // Transforms the coordinates of the origin of the plane, into the
-    // coordinates of the axial geometry
-    const Vector& origin = plane.GetOrigin();
-    Vector p = LinearAlgebra::Product(
-      transformInverse_,
-      LinearAlgebra::CreateVector(origin[0], origin[1], origin[2], 1));
-
-    assert(LinearAlgebra::IsNear(p[3], 1));
-
-    double z;
-
-    switch (projection)
-    {
-      case VolumeProjection_Axial:
-        z = p[2];
-        break;
-
-      case VolumeProjection_Coronal:
-        z = p[1];
-        break;
-
-      case VolumeProjection_Sagittal:
-        z = p[0];
-        break;
-
-      default:
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-
-    const unsigned int projectionDepth = GetProjectionDepth(projection);
-    
-    z *= static_cast<double>(projectionDepth);
-    if (z < 0)
-    {
-      return false;
-    }
-        
-    unsigned int d = static_cast<unsigned int>(std::floor(z));
-    if (d >= projectionDepth)
-    {
-      return false;
-    }
-    else
-    {
-      slice = d;
-      return true;
-    }
-  }
-}
--- a/Framework/Toolbox/VolumeImageGeometry.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,122 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "../StoneEnumerations.h"
-#include "CoordinateSystem3D.h"
-
-namespace OrthancStone
-{
-  class VolumeImageGeometry
-  {
-  private:
-    unsigned int           width_;
-    unsigned int           height_;
-    unsigned int           depth_;
-    CoordinateSystem3D     axialGeometry_;
-    CoordinateSystem3D     coronalGeometry_;
-    CoordinateSystem3D     sagittalGeometry_;
-    Vector                 voxelDimensions_;
-    Matrix                 transform_;
-    Matrix                 transformInverse_;
-
-    void Invalidate();
-
-  public:
-    VolumeImageGeometry();
-
-    unsigned int GetWidth() const
-    {
-      return width_;
-    }
-
-    unsigned int GetHeight() const
-    {
-      return height_;
-    }
-
-    unsigned int GetDepth() const
-    {
-      return depth_;
-    }
-
-    const CoordinateSystem3D& GetAxialGeometry() const
-    {
-      return axialGeometry_;
-    }
-
-    const CoordinateSystem3D& GetCoronalGeometry() const
-    {
-      return coronalGeometry_;
-    }
-
-    const CoordinateSystem3D& GetSagittalGeometry() const
-    {
-      return sagittalGeometry_;
-    }
-
-    const CoordinateSystem3D& GetProjectionGeometry(VolumeProjection projection) const;
-    
-    const Matrix& GetTransform() const
-    {
-      return transform_;
-    }
-
-    const Matrix& GetTransformInverse() const
-    {
-      return transformInverse_;
-    }
-
-    void SetSize(unsigned int width,
-                 unsigned int height,
-                 unsigned int depth);
-
-    // Set the geometry of the first axial slice (i.e. the one whose
-    // depth == 0)
-    void SetAxialGeometry(const CoordinateSystem3D& geometry);
-
-    void SetVoxelDimensions(double x,
-                            double y,
-                            double z);
-
-    Vector GetVoxelDimensions(VolumeProjection projection) const;
-
-    unsigned int GetProjectionWidth(VolumeProjection projection) const;
-
-    unsigned int GetProjectionHeight(VolumeProjection projection) const;
-
-    unsigned int GetProjectionDepth(VolumeProjection projection) const;
-
-    // Get the 3D position of a point in the volume, where x, y and z
-    // lie in the [0;1] range
-    Vector GetCoordinates(float x,
-                          float y,
-                          float z) const;
-
-    bool DetectProjection(VolumeProjection& projection,
-                          const Vector& planeNormal) const;
-
-    bool DetectSlice(VolumeProjection& projection,
-                     unsigned int& slice,
-                     const CoordinateSystem3D& plane) const;
-  };
-}
--- a/Framework/Viewport/IMouseTracker.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "CairoSurface.h"
-#include <vector>
-
-namespace OrthancStone
-{
-  struct Touch
-  {
-    float x;
-    float y;
-
-    Touch(float x, float y)
-    : x(x),
-      y(y)
-    {
-    }
-    Touch()
-      : x(0.0f),
-        y(0.0f)
-    {
-    }
-  };
-
-
-  // this is tracking a mouse in screen coordinates/pixels unlike
-  // the IWorldSceneMouseTracker that is tracking a mouse
-  // in scene coordinates/mm.
-  class IMouseTracker : public boost::noncopyable
-  {
-  public:
-    virtual ~IMouseTracker()
-    {
-    }
-    
-    virtual void Render(Orthanc::ImageAccessor& surface) = 0;
-
-    virtual void MouseUp() = 0;
-
-    // Returns "true" iff. the background scene must be repainted
-    virtual void MouseMove(int x, 
-                           int y,
-                           const std::vector<Touch>& displayTouches) = 0;
-  };
-}
--- a/Framework/Viewport/IStatusBar.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include <string>
-#include <boost/noncopyable.hpp>
-
-namespace OrthancStone
-{
-  class IStatusBar : public boost::noncopyable
-  {
-  public:
-    virtual ~IStatusBar()
-    {
-    }
-    
-    virtual void ClearMessage() = 0;
-
-    virtual void SetMessage(const std::string& message) = 0;
-  };
-}
--- a/Framework/Viewport/IViewport.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,95 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "IStatusBar.h"
-#include "../StoneEnumerations.h"
-#include "../Messages/IObservable.h"
-
-#include <Core/Images/ImageAccessor.h>
-#include "../Viewport/IMouseTracker.h" // only to get the "Touch" definition
-
-namespace OrthancStone
-{
-  class IWidget;   // Forward declaration
-  
-  class IViewport : public IObservable
-  {
-  public:
-    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, ViewportChangedMessage, IViewport);
-
-    IViewport(MessageBroker& broker) :
-      IObservable(broker)
-    {
-    }
-    
-    virtual ~IViewport()
-    {
-    }
-
-    virtual void FitContent() = 0;
-
-    virtual void SetStatusBar(IStatusBar& statusBar) = 0;
-
-    virtual void SetSize(unsigned int width,
-                         unsigned int height) = 0;
-
-    // The function returns "true" iff. a new frame was rendered
-    virtual bool Render(Orthanc::ImageAccessor& surface) = 0;
-
-    virtual void MouseDown(MouseButton button,
-                           int x,
-                           int y,
-                           KeyboardModifiers modifiers,
-                           const std::vector<Touch>& touches) = 0;
-
-    virtual void MouseUp() = 0;
-
-    virtual void MouseMove(int x, 
-                           int y,
-                           const std::vector<Touch>& displayTouches) = 0;
-
-    virtual void MouseEnter() = 0;
-
-    virtual void MouseLeave() = 0;
-
-    virtual void MouseWheel(MouseWheelDirection direction,
-                            int x,
-                            int y,
-                            KeyboardModifiers modifiers) = 0;
-
-    virtual void KeyPressed(KeyboardKeys key,
-                            char keyChar,
-                            KeyboardModifiers modifiers) = 0;
-
-    virtual bool HasAnimation() = 0;
-
-    virtual void DoAnimation() = 0;
-
-    // Should only be called from IWidget
-    // TODO Why should this be virtual?
-    virtual void NotifyContentChanged()
-    {
-      BroadcastMessage(ViewportChangedMessage(*this));
-    }
-  };
-}
--- a/Framework/Viewport/WidgetViewport.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,289 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "WidgetViewport.h"
-
-#include <Core/Images/ImageProcessing.h>
-#include <Core/OrthancException.h>
-
-namespace OrthancStone
-{
-  WidgetViewport::WidgetViewport(MessageBroker& broker) :
-    IViewport(broker),
-    statusBar_(NULL),
-    isMouseOver_(false),
-    lastMouseX_(0),
-    lastMouseY_(0),
-    backgroundChanged_(false)
-  {
-  }
-
-
-  void WidgetViewport::FitContent()
-  {
-    if (centralWidget_.get() != NULL)
-    {
-      centralWidget_->FitContent();
-    }
-  }
-
-
-  void WidgetViewport::SetStatusBar(IStatusBar& statusBar)
-  {
-    statusBar_ = &statusBar;
-
-    if (centralWidget_.get() != NULL)
-    {
-      centralWidget_->SetStatusBar(statusBar);
-    }
-  }
-
-
-  IWidget& WidgetViewport::SetCentralWidget(IWidget* widget)
-  {
-    if (widget == NULL)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-
-    mouseTracker_.reset(NULL);
-
-    centralWidget_.reset(widget);
-    centralWidget_->SetViewport(*this);
-
-    if (statusBar_ != NULL)
-    {
-      centralWidget_->SetStatusBar(*statusBar_);
-    }
-
-    NotifyBackgroundChanged();
-
-    return *widget;
-  }
-
-
-  void WidgetViewport::NotifyBackgroundChanged()
-  {
-    backgroundChanged_ = true;
-    NotifyContentChanged();
-  }
-
-
-  void WidgetViewport::SetSize(unsigned int width,
-                               unsigned int height)
-  {
-    background_.SetSize(width, height, false /* no alpha */);
-
-    if (centralWidget_.get() != NULL)
-    {
-      centralWidget_->SetSize(width, height);
-    }
-
-    NotifyBackgroundChanged();
-  }
-
-
-  bool WidgetViewport::Render(Orthanc::ImageAccessor& surface)
-  {
-    if (centralWidget_.get() == NULL)
-    {
-      return false;
-    }
-
-    Orthanc::ImageAccessor background;
-    background_.GetWriteableAccessor(background);
-
-    if (backgroundChanged_ &&
-        !centralWidget_->Render(background))
-    {
-      return false;
-    }
-
-    if (background.GetWidth() != surface.GetWidth() ||
-        background.GetHeight() != surface.GetHeight())
-    {
-      return false;
-    }
-
-    Orthanc::ImageProcessing::Convert(surface, background);
-    
-    if (mouseTracker_.get() != NULL)
-    {
-      mouseTracker_->Render(surface);
-    }
-    else if (isMouseOver_)
-    {
-      centralWidget_->RenderMouseOver(surface, lastMouseX_, lastMouseY_);
-    }
-
-    return true;
-  }
-
-  void WidgetViewport::TouchStart(const std::vector<Touch>& displayTouches)
-  {
-    MouseDown(MouseButton_Left, (int)displayTouches[0].x, (int)displayTouches[0].y, KeyboardModifiers_None, displayTouches); // one touch is equivalent to a mouse tracker without left button -> set the mouse coordinates to the first touch coordinates
-  }
-      
-  void WidgetViewport::TouchMove(const std::vector<Touch>& displayTouches)
-  {
-    MouseMove((int)displayTouches[0].x, (int)displayTouches[0].y, displayTouches); // one touch is equivalent to a mouse tracker without left button -> set the mouse coordinates to the first touch coordinates
-  }
-      
-  void WidgetViewport::TouchEnd(const std::vector<Touch>& displayTouches)
-  {
-    // note: TouchEnd is not triggered when a single touch gesture ends (it is only triggered when
-    // going from 2 touches to 1 touch, ...)
-    MouseUp();
-  }
-
-  void WidgetViewport::MouseDown(MouseButton button,
-                                 int x,
-                                 int y,
-                                 KeyboardModifiers modifiers,
-                                 const std::vector<Touch>& displayTouches
-                                 )
-  {
-    lastMouseX_ = x;
-    lastMouseY_ = y;
-
-    if (centralWidget_.get() != NULL)
-    {
-      mouseTracker_.reset(centralWidget_->CreateMouseTracker(button, x, y, modifiers, displayTouches));
-    }
-    else
-    {
-      mouseTracker_.reset(NULL);
-    }      
-
-    NotifyContentChanged();
-  }
-
-
-  void WidgetViewport::MouseUp()
-  {
-    if (mouseTracker_.get() != NULL)
-    {
-      mouseTracker_->MouseUp();
-      mouseTracker_.reset(NULL);
-      NotifyContentChanged();
-    }
-  }
-
-
-  void WidgetViewport::MouseMove(int x, 
-                                 int y,
-                                 const std::vector<Touch>& displayTouches)
-  {
-    if (centralWidget_.get() == NULL)
-    {
-      return;
-    }
-
-    lastMouseX_ = x;
-    lastMouseY_ = y;
-
-    bool repaint = false;
-    
-    if (mouseTracker_.get() != NULL)
-    {
-      mouseTracker_->MouseMove(x, y, displayTouches);
-      repaint = true;
-    }
-    else
-    {
-      repaint = centralWidget_->HasRenderMouseOver();
-    }
-
-    if (repaint)
-    {
-      // The scene must be repainted, notify the observers
-      NotifyContentChanged();
-    }
-  }
-
-
-  void WidgetViewport::MouseEnter()
-  {
-    isMouseOver_ = true;
-    NotifyContentChanged();
-  }
-
-
-  void WidgetViewport::MouseLeave()
-  {
-    isMouseOver_ = false;
-
-    if (mouseTracker_.get() != NULL)
-    {
-      mouseTracker_->MouseUp();
-      mouseTracker_.reset(NULL);
-    }
-
-    NotifyContentChanged();
-  }
-
-
-  void WidgetViewport::MouseWheel(MouseWheelDirection direction,
-                                  int x,
-                                  int y,
-                                  KeyboardModifiers modifiers)
-  {
-    if (centralWidget_.get() != NULL &&
-        mouseTracker_.get() == NULL)
-    {
-      centralWidget_->MouseWheel(direction, x, y, modifiers);
-    }
-  }
-
-
-  void WidgetViewport::KeyPressed(KeyboardKeys key,
-                                  char keyChar,
-                                  KeyboardModifiers modifiers)
-  {
-    if (centralWidget_.get() != NULL &&
-        mouseTracker_.get() == NULL)
-    {
-      centralWidget_->KeyPressed(key, keyChar, modifiers);
-    }
-  }
-
-
-  bool WidgetViewport::HasAnimation()
-  {
-    if (centralWidget_.get() != NULL)
-    {
-      return centralWidget_->HasAnimation();
-    }
-    else
-    {
-      return false;
-    }
-  }
-   
-
-  void WidgetViewport::DoAnimation()
-  {
-    if (centralWidget_.get() != NULL)
-    {
-      centralWidget_->DoAnimation();
-    }
-  }
-}
--- a/Framework/Viewport/WidgetViewport.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,94 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "IViewport.h"
-#include "../Widgets/IWidget.h"
-
-#include <memory>
-
-namespace OrthancStone
-{
-  class WidgetViewport : public IViewport
-  {
-  private:
-    std::auto_ptr<IWidget>        centralWidget_;
-    IStatusBar*                   statusBar_;
-    std::auto_ptr<IMouseTracker>  mouseTracker_;
-    bool                          isMouseOver_;
-    int                           lastMouseX_;
-    int                           lastMouseY_;
-    CairoSurface                  background_;
-    bool                          backgroundChanged_;
-
-  public:
-    WidgetViewport(MessageBroker& broker);
-
-    virtual void FitContent();
-
-    virtual void SetStatusBar(IStatusBar& statusBar);
-
-    IWidget& SetCentralWidget(IWidget* widget);  // Takes ownership
-
-    virtual void NotifyBackgroundChanged();
-
-    virtual void SetSize(unsigned int width,
-                         unsigned int height);
-
-    virtual bool Render(Orthanc::ImageAccessor& surface);
-
-    virtual void MouseDown(MouseButton button,
-                           int x,
-                           int y,
-                           KeyboardModifiers modifiers,
-                           const std::vector<Touch>& displayTouches);
-
-    virtual void MouseUp();
-
-    virtual void MouseMove(int x, 
-                           int y,
-                           const std::vector<Touch>& displayTouches);
-
-    virtual void MouseEnter();
-
-    virtual void MouseLeave();
-
-    virtual void TouchStart(const std::vector<Touch>& touches);
-    
-    virtual void TouchMove(const std::vector<Touch>& touches);
-    
-    virtual void TouchEnd(const std::vector<Touch>& touches);
-
-    virtual void MouseWheel(MouseWheelDirection direction,
-                            int x,
-                            int y,
-                            KeyboardModifiers modifiers);
-
-    virtual void KeyPressed(KeyboardKeys key,
-                            char keyChar,
-                            KeyboardModifiers modifiers);
-
-    virtual bool HasAnimation();
-
-    virtual void DoAnimation();
-  };
-}
--- a/Framework/Volumes/ISlicedVolume.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,77 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "../Messages/IObservable.h"
-#include "../Toolbox/Slice.h"
-
-namespace OrthancStone
-{
-  class ISlicedVolume : public IObservable
-  {
-  public:
-    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, ContentChangedMessage, ISlicedVolume);
-    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryErrorMessage, ISlicedVolume);
-    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryReadyMessage, ISlicedVolume);
-    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, VolumeReadyMessage, ISlicedVolume);
-
-
-    class SliceContentChangedMessage : public OriginMessage<ISlicedVolume>
-    {
-      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
-      
-    private:
-      size_t        sliceIndex_;
-      const Deprecated::Slice&  slice_;
-      
-    public:
-      SliceContentChangedMessage(ISlicedVolume& origin,
-                                 size_t sliceIndex,
-                                 const Deprecated::Slice& slice) :
-        OriginMessage(origin),
-        sliceIndex_(sliceIndex),
-        slice_(slice)
-      {
-      }
-
-      size_t GetSliceIndex() const
-      {
-        return sliceIndex_;
-      }
-
-      const Deprecated::Slice& GetSlice() const
-      {
-        return slice_;
-      }
-    };
-
-
-    ISlicedVolume(MessageBroker& broker) :
-      IObservable(broker)
-    {
-    }
-    
-    virtual size_t GetSliceCount() const = 0;
-
-    virtual const Deprecated::Slice& GetSlice(size_t slice) const = 0;
-  };
-}
--- a/Framework/Volumes/IVolumeLoader.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "../Messages/IObservable.h"
-
-namespace OrthancStone
-{
-  class IVolumeLoader : public IObservable
-  {
-  public:
-    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryReadyMessage, IVolumeLoader);
-    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryErrorMessage, IVolumeLoader);
-    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, ContentChangedMessage, IVolumeLoader);
-
-    IVolumeLoader(MessageBroker& broker) :
-      IObservable(broker)
-    {
-    }
-  };
-}
--- a/Framework/Volumes/ImageBuffer3D.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Volumes/ImageBuffer3D.cpp	Wed May 22 16:13:46 2019 +0200
@@ -21,8 +21,6 @@
 
 #include "ImageBuffer3D.h"
 
-#include "../Toolbox/GeometryToolbox.h"
-
 #include <Core/Images/ImageProcessing.h>
 #include <Core/Logging.h>
 #include <Core/OrthancException.h>
@@ -118,8 +116,6 @@
     computeRange_(computeRange),
     hasRange_(false)
   {
-    geometry_.SetSize(width, height, depth);
-
     LOG(INFO) << "Created a 3D image of size " << width << "x" << height
               << "x" << depth << " in " << Orthanc::EnumerationToString(format)
               << " (" << (GetEstimatedMemorySize() / (1024ll * 1024ll)) << "MB)";
@@ -132,63 +128,6 @@
   }
 
 
-
-
-  ParallelSlices* ImageBuffer3D::GetGeometry(VolumeProjection projection) const
-  {
-    const Vector dimensions = geometry_.GetVoxelDimensions(VolumeProjection_Axial);
-    const CoordinateSystem3D& axial = geometry_.GetAxialGeometry();
-    
-    std::auto_ptr<ParallelSlices> result(new ParallelSlices);
-
-    switch (projection)
-    {
-      case VolumeProjection_Axial:
-        for (unsigned int z = 0; z < depth_; z++)
-        {
-          Vector origin = axial.GetOrigin();
-          origin += static_cast<double>(z) * dimensions[2] * axial.GetNormal();
-
-          result->AddSlice(origin,
-                           axial.GetAxisX(),
-                           axial.GetAxisY());
-        }
-        break;
-
-      case VolumeProjection_Coronal:
-        for (unsigned int y = 0; y < height_; y++)
-        {
-          Vector origin = axial.GetOrigin();
-          origin += static_cast<double>(y) * dimensions[1] * axial.GetAxisY();
-          origin += static_cast<double>(depth_ - 1) * dimensions[2] * axial.GetNormal();
-
-          result->AddSlice(origin,
-                           axial.GetAxisX(),
-                           -axial.GetNormal());
-        }
-        break;
-
-      case VolumeProjection_Sagittal:
-        for (unsigned int x = 0; x < width_; x++)
-        {
-          Vector origin = axial.GetOrigin();
-          origin += static_cast<double>(x) * dimensions[0] * axial.GetAxisX();
-          origin += static_cast<double>(depth_ - 1) * dimensions[2] * axial.GetNormal();
-
-          result->AddSlice(origin,
-                           axial.GetAxisY(),
-                           -axial.GetNormal());
-        }
-        break;
-
-      default:
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-
-    return result.release();
-  }
-
-
   uint64_t ImageBuffer3D::GetEstimatedMemorySize() const
   {
     return image_.GetPitch() * image_.GetHeight() * Orthanc::GetBytesPerPixel(format_);
@@ -258,35 +197,6 @@
   }
 
 
-  bool ImageBuffer3D::FitWindowingToRange(RenderStyle& style,
-                                          const Deprecated::DicomFrameConverter& converter) const
-  {
-    if (hasRange_)
-    {
-      style.windowing_ = ImageWindowing_Custom;
-      
-      // casting the narrower type to wider before calling the + operator
-      // will prevent overflowing (this is why the cast to double is only 
-      // done on the first operand)
-      style.customWindowCenter_ = static_cast<float>(
-        converter.Apply((static_cast<double>(minValue_) + maxValue_) / 2.0));
-      
-      style.customWindowWidth_ = static_cast<float>(
-        converter.Apply(static_cast<double>(maxValue_) - minValue_));
-      
-      if (style.customWindowWidth_ > 1)
-      {
-        return true;
-      }
-    }
-
-    style.windowing_ = ImageWindowing_Custom;
-    style.customWindowCenter_ = 128.0;
-    style.customWindowWidth_ = 256.0;
-    return false;
-  }
-
-
   ImageBuffer3D::SliceReader::SliceReader(const ImageBuffer3D& that,
                                           VolumeProjection projection,
                                           unsigned int slice)
--- a/Framework/Volumes/ImageBuffer3D.h	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Volumes/ImageBuffer3D.h	Wed May 22 16:13:46 2019 +0200
@@ -22,10 +22,7 @@
 #pragma once
 
 #include "../StoneEnumerations.h"
-#include "../Layers/RenderStyle.h"
-#include "../Toolbox/VolumeImageGeometry.h"
-#include "../Toolbox/DicomFrameConverter.h"
-#include "../Toolbox/ParallelSlices.h"
+#include "../Toolbox/LinearAlgebra.h"
 
 #include <Core/Images/Image.h>
 
@@ -34,7 +31,6 @@
   class ImageBuffer3D : public boost::noncopyable
   {
   private:
-    VolumeImageGeometry    geometry_;  // TODO => Move this out of this class
     Orthanc::Image         image_;
     Orthanc::PixelFormat   format_;
     unsigned int           width_;
@@ -78,16 +74,6 @@
 
     void Clear();
 
-    VolumeImageGeometry& GetGeometry()
-    {
-      return geometry_;
-    }
-
-    const VolumeImageGeometry& GetGeometry() const
-    {
-      return geometry_;
-    }
-
     const Orthanc::ImageAccessor& GetInternalImage() const
     {
       return image_;
@@ -113,17 +99,11 @@
       return format_;
     }
 
-    // TODO - Remove
-    ParallelSlices* GetGeometry(VolumeProjection projection) const;
-    
     uint64_t GetEstimatedMemorySize() const;
 
     bool GetRange(float& minValue,
                   float& maxValue) const;
 
-    bool FitWindowingToRange(RenderStyle& style,
-                             const Deprecated::DicomFrameConverter& converter) const;
-
     uint8_t GetVoxelGrayscale8Unchecked(unsigned int x,
                                         unsigned int y,
                                         unsigned int z) const
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Volumes/OrientedVolumeBoundingBox.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,268 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "OrientedVolumeBoundingBox.h"
+
+#include "../Toolbox/GeometryToolbox.h"
+#include "ImageBuffer3D.h"
+
+#include <Core/OrthancException.h>
+
+#include <cassert>
+
+namespace OrthancStone
+{
+  OrientedVolumeBoundingBox::OrientedVolumeBoundingBox(const VolumeImageGeometry& geometry)
+  {
+    unsigned int n = geometry.GetDepth();
+    if (n < 1)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageSize);      
+    }
+
+    Vector dim = geometry.GetVoxelDimensions(VolumeProjection_Axial);
+
+    u_ = geometry.GetAxialGeometry().GetAxisX();
+    v_ = geometry.GetAxialGeometry().GetAxisY();
+    w_ = geometry.GetAxialGeometry().GetNormal();
+
+    hu_ = static_cast<double>(geometry.GetWidth() * dim[0] / 2.0);
+    hv_ = static_cast<double>(geometry.GetHeight() * dim[1] / 2.0);
+    hw_ = static_cast<double>(geometry.GetDepth() * dim[2] / 2.0);
+      
+    c_ = (geometry.GetAxialGeometry().GetOrigin() + 
+          (hu_ - dim[0] / 2.0) * u_ +
+          (hv_ - dim[1] / 2.0) * v_ +
+          (hw_ - dim[2] / 2.0) * w_);
+  }
+
+
+  bool OrientedVolumeBoundingBox::HasIntersectionWithPlane(std::vector<Vector>& points,
+                                                           const Vector& normal,
+                                                           double d) const
+  {
+    assert(normal.size() == 3);
+
+    double r = (hu_ * fabs(boost::numeric::ublas::inner_prod(normal, u_)) +
+                hv_ * fabs(boost::numeric::ublas::inner_prod(normal, v_)) +
+                hw_ * fabs(boost::numeric::ublas::inner_prod(normal, w_)));
+
+    double s = boost::numeric::ublas::inner_prod(normal, c_) + d;
+
+    if (fabs(s) >= r)
+    {
+      // No intersection, or intersection is reduced to a single point
+      return false;
+    }
+    else
+    {
+      Vector p;
+
+      // Loop over all the 12 edges (segments) of the oriented
+      // bounding box, and check whether they intersect the plane
+        
+      // X-aligned edges
+      if (GeometryToolbox::IntersectPlaneAndSegment
+          (p, normal, d,
+           c_ - u_ * hu_ - v_ * hv_ - w_ * hw_,
+           c_ + u_ * hu_ - v_ * hv_ - w_ * hw_))
+      {
+        points.push_back(p);
+      }
+
+      if (GeometryToolbox::IntersectPlaneAndSegment
+          (p, normal, d,
+           c_ - u_ * hu_ + v_ * hv_ - w_ * hw_,
+           c_ + u_ * hu_ + v_ * hv_ - w_ * hw_))
+      {
+        points.push_back(p);
+      }
+
+      if (GeometryToolbox::IntersectPlaneAndSegment
+          (p, normal, d,
+           c_ - u_ * hu_ - v_ * hv_ + w_ * hw_,
+           c_ + u_ * hu_ - v_ * hv_ + w_ * hw_))
+      {
+        points.push_back(p);
+      }
+
+      if (GeometryToolbox::IntersectPlaneAndSegment
+          (p, normal, d,
+           c_ - u_ * hu_ + v_ * hv_ + w_ * hw_,
+           c_ + u_ * hu_ + v_ * hv_ + w_ * hw_))
+      {
+        points.push_back(p);
+      }
+
+      // Y-aligned edges
+      if (GeometryToolbox::IntersectPlaneAndSegment
+          (p, normal, d,
+           c_ - u_ * hu_ - v_ * hv_ - w_ * hw_,
+           c_ - u_ * hu_ + v_ * hv_ - w_ * hw_))
+      {
+        points.push_back(p);
+      }
+
+      if (GeometryToolbox::IntersectPlaneAndSegment
+          (p, normal, d,
+           c_ + u_ * hu_ - v_ * hv_ - w_ * hw_,
+           c_ + u_ * hu_ + v_ * hv_ - w_ * hw_))
+      {
+        points.push_back(p);
+      }
+
+      if (GeometryToolbox::IntersectPlaneAndSegment
+          (p, normal, d,
+           c_ - u_ * hu_ - v_ * hv_ + w_ * hw_,
+           c_ - u_ * hu_ + v_ * hv_ + w_ * hw_))
+      {
+        points.push_back(p);
+      }
+
+      if (GeometryToolbox::IntersectPlaneAndSegment
+          (p, normal, d,
+           c_ + u_ * hu_ - v_ * hv_ + w_ * hw_,
+           c_ + u_ * hu_ + v_ * hv_ + w_ * hw_))
+      {
+        points.push_back(p);
+      }
+
+      // Z-aligned edges
+      if (GeometryToolbox::IntersectPlaneAndSegment
+          (p, normal, d,
+           c_ - u_ * hu_ - v_ * hv_ - w_ * hw_,
+           c_ - u_ * hu_ - v_ * hv_ + w_ * hw_))
+      {
+        points.push_back(p);
+      }
+
+      if (GeometryToolbox::IntersectPlaneAndSegment
+          (p, normal, d,
+           c_ + u_ * hu_ - v_ * hv_ - w_ * hw_,
+           c_ + u_ * hu_ - v_ * hv_ + w_ * hw_))
+      {
+        points.push_back(p);
+      }
+
+      if (GeometryToolbox::IntersectPlaneAndSegment
+          (p, normal, d,
+           c_ - u_ * hu_ + v_ * hv_ - w_ * hw_,
+           c_ - u_ * hu_ + v_ * hv_ + w_ * hw_))
+      {
+        points.push_back(p);
+      }
+
+      if (GeometryToolbox::IntersectPlaneAndSegment
+          (p, normal, d,
+           c_ + u_ * hu_ + v_ * hv_ - w_ * hw_,
+           c_ + u_ * hu_ + v_ * hv_ + w_ * hw_))
+      {
+        points.push_back(p);
+      }
+
+      return true;
+    }
+  }
+
+
+  bool OrientedVolumeBoundingBox::HasIntersection(std::vector<Vector>& points,
+                                                  const CoordinateSystem3D& plane) const
+  {
+    // From the vector equation of a 3D plane (specified by origin
+    // and normal), to the general equation of a 3D plane (which
+    // looses information about the origin of the coordinate system)
+    const Vector& normal = plane.GetNormal();
+    const Vector& origin = plane.GetOrigin();
+    double d = -(normal[0] * origin[0] + normal[1] * origin[1] + normal[2] * origin[2]);
+
+    return HasIntersectionWithPlane(points, normal, d);
+  }
+  
+
+  bool OrientedVolumeBoundingBox::Contains(const Vector& p) const
+  {
+    assert(p.size() == 3);
+
+    const Vector q = p - c_;
+
+    return (fabs(boost::numeric::ublas::inner_prod(q, u_)) <= hu_ &&
+            fabs(boost::numeric::ublas::inner_prod(q, v_)) <= hv_ &&
+            fabs(boost::numeric::ublas::inner_prod(q, w_)) <= hw_);
+  }
+
+  
+  void OrientedVolumeBoundingBox::FromInternalCoordinates(Vector& target,
+                                                          double x,
+                                                          double y,
+                                                          double z) const
+  {
+    target = (c_ +
+              u_ * 2.0 * hu_ * (x - 0.5) +
+              v_ * 2.0 * hv_ * (y - 0.5) +
+              w_ * 2.0 * hw_ * (z - 0.5));
+  }
+
+  
+  void OrientedVolumeBoundingBox::FromInternalCoordinates(Vector& target,
+                                                          const Vector& source) const
+  {
+    assert(source.size() == 3);
+    FromInternalCoordinates(target, source[0], source[1], source[2]);
+  }
+
+  
+  void OrientedVolumeBoundingBox::ToInternalCoordinates(Vector& target,
+                                                        const Vector& source) const
+  {
+    assert(source.size() == 3);
+    const Vector q = source - c_;
+
+    double x = boost::numeric::ublas::inner_prod(q, u_) / (2.0 * hu_) + 0.5;
+    double y = boost::numeric::ublas::inner_prod(q, v_) / (2.0 * hv_) + 0.5;
+    double z = boost::numeric::ublas::inner_prod(q, w_) / (2.0 * hw_) + 0.5;
+
+    LinearAlgebra::AssignVector(target, x, y, z);
+  }
+
+
+  bool OrientedVolumeBoundingBox::ComputeExtent(Extent2D& extent,
+                                                const CoordinateSystem3D& plane) const
+  {
+    extent.Reset();
+    
+    std::vector<Vector> points;
+    if (HasIntersection(points, plane))
+    {
+      for (size_t i = 0; i < points.size(); i++)
+      {
+        double x, y;
+        plane.ProjectPoint(x, y, points[i]);
+        extent.AddPoint(x, y);
+      }
+      
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Volumes/OrientedVolumeBoundingBox.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,74 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../Toolbox/CoordinateSystem3D.h"
+#include "../Toolbox/Extent2D.h"
+#include "../Toolbox/LinearAlgebra.h"
+#include "VolumeImageGeometry.h"
+
+namespace OrthancStone
+{
+  class OrientedVolumeBoundingBox : public boost::noncopyable
+  {
+  private:
+    Vector  c_;   // center
+    Vector  u_;   // normalized width vector
+    Vector  v_;   // normalized height vector
+    Vector  w_;   // normalized depth vector
+    double  hu_;  // half width
+    double  hv_;  // half height
+    double  hw_;  // half depth
+
+  public:
+    OrientedVolumeBoundingBox(const VolumeImageGeometry& geometry);
+
+    const Vector& GetCenter() const
+    {
+      return c_;
+    }
+
+    bool HasIntersectionWithPlane(std::vector<Vector>& points,
+                                  const Vector& normal,
+                                  double d) const;
+
+    bool HasIntersection(std::vector<Vector>& points,
+                         const CoordinateSystem3D& plane) const;
+
+    bool Contains(const Vector& p) const;
+
+    void FromInternalCoordinates(Vector& target,
+                                 double x,
+                                 double y,
+                                 double z) const;
+
+    void FromInternalCoordinates(Vector& target,
+                                 const Vector& source) const;
+
+    void ToInternalCoordinates(Vector& target,
+                               const Vector& source) const;
+
+    bool ComputeExtent(Extent2D& extent,
+                       const CoordinateSystem3D& plane) const;
+  };
+}
+
--- a/Framework/Volumes/StructureSetLoader.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,116 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "StructureSetLoader.h"
-
-#include "../Toolbox/MessagingToolbox.h"
-
-#include <Core/OrthancException.h>
-
-namespace OrthancStone
-{
-  StructureSetLoader::StructureSetLoader(MessageBroker& broker,
-                                         OrthancApiClient& orthanc) :
-    IVolumeLoader(broker),
-    IObserver(broker),
-    orthanc_(orthanc)
-  {
-  }
-  
-
-  void StructureSetLoader::OnReferencedSliceLoaded(const OrthancApiClient::JsonResponseReadyMessage& message)
-  {
-    OrthancPlugins::FullOrthancDataset dataset(message.GetJson());
-
-    Orthanc::DicomMap slice;
-    MessagingToolbox::ConvertDataset(slice, dataset);
-    structureSet_->AddReferencedSlice(slice);
-
-    BroadcastMessage(ContentChangedMessage(*this));
-  }
-  
-
-  void StructureSetLoader::OnStructureSetLoaded(const OrthancApiClient::JsonResponseReadyMessage& message)
-  {
-    OrthancPlugins::FullOrthancDataset dataset(message.GetJson());
-    structureSet_.reset(new DicomStructureSet(dataset));
-
-    std::set<std::string> instances;
-    structureSet_->GetReferencedInstances(instances);
-
-    for (std::set<std::string>::const_iterator it = instances.begin();
-         it != instances.end(); ++it)
-    {
-      orthanc_.PostBinaryAsyncExpectJson("/tools/lookup", *it,
-                            new Callable<StructureSetLoader, OrthancApiClient::JsonResponseReadyMessage>(*this, &StructureSetLoader::OnLookupCompleted));
-    }
-
-    BroadcastMessage(GeometryReadyMessage(*this));
-  }
-
-  
-  void StructureSetLoader::OnLookupCompleted(const OrthancApiClient::JsonResponseReadyMessage& message)
-  {
-    const Json::Value& lookup = message.GetJson();
-
-    if (lookup.type() != Json::arrayValue ||
-        lookup.size() != 1 ||
-        !lookup[0].isMember("Type") ||
-        !lookup[0].isMember("Path") ||
-        lookup[0]["Type"].type() != Json::stringValue ||
-        lookup[0]["ID"].type() != Json::stringValue ||
-        lookup[0]["Type"].asString() != "Instance")
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
-    }
-
-    const std::string& instance = lookup[0]["ID"].asString();
-    orthanc_.GetJsonAsync("/instances/" + instance + "/tags",
-                          new Callable<StructureSetLoader, OrthancApiClient::JsonResponseReadyMessage>(*this, &StructureSetLoader::OnReferencedSliceLoaded));
-  }
-
-  
-  void StructureSetLoader::ScheduleLoadInstance(const std::string& instance)
-  {
-    if (structureSet_.get() != NULL)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-    else
-    {
-      orthanc_.GetJsonAsync("/instances/" + instance + "/tags?ignore-length=3006-0050",
-                            new Callable<StructureSetLoader, OrthancApiClient::JsonResponseReadyMessage>(*this, &StructureSetLoader::OnStructureSetLoaded));
-    }
-  }
-
-
-  DicomStructureSet& StructureSetLoader::GetStructureSet()
-  {
-    if (structureSet_.get() == NULL)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-    else
-    {
-      return *structureSet_;
-    }
-  }
-}
--- a/Framework/Volumes/StructureSetLoader.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "../Toolbox/DicomStructureSet.h"
-#include "../Toolbox/OrthancApiClient.h"
-#include "IVolumeLoader.h"
-
-namespace OrthancStone
-{
-  class StructureSetLoader :
-    public IVolumeLoader,
-    public IObserver
-  {
-  private:
-    OrthancApiClient&                 orthanc_;
-    std::auto_ptr<DicomStructureSet>  structureSet_;
-
-    void OnReferencedSliceLoaded(const OrthancApiClient::JsonResponseReadyMessage& message);
-
-    void OnStructureSetLoaded(const OrthancApiClient::JsonResponseReadyMessage& message);
-
-    void OnLookupCompleted(const OrthancApiClient::JsonResponseReadyMessage& message);
-
-  public:
-    StructureSetLoader(MessageBroker& broker,
-                       OrthancApiClient& orthanc);
-
-    void ScheduleLoadInstance(const std::string& instance);
-
-    bool HasStructureSet() const
-    {
-      return structureSet_.get() != NULL;
-    }
-
-    DicomStructureSet& GetStructureSet();
-  };
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Volumes/VolumeImageGeometry.cpp	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,309 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "VolumeImageGeometry.h"
+
+#include "../Toolbox/GeometryToolbox.h"
+
+#include <Core/OrthancException.h>
+
+
+namespace OrthancStone
+{
+  void VolumeImageGeometry::Invalidate()
+  {
+    Vector p = (axialGeometry_.GetOrigin() +
+                static_cast<double>(depth_ - 1) * voxelDimensions_[2] * axialGeometry_.GetNormal());
+        
+    coronalGeometry_ = CoordinateSystem3D(p,
+                                          axialGeometry_.GetAxisX(),
+                                          -axialGeometry_.GetNormal());
+    
+    sagittalGeometry_ = CoordinateSystem3D(p,
+                                           axialGeometry_.GetAxisY(),
+                                           axialGeometry_.GetNormal());
+
+    Vector origin = (
+      axialGeometry_.MapSliceToWorldCoordinates(-0.5 * voxelDimensions_[0],
+                                                -0.5 * voxelDimensions_[1]) -
+      0.5 * voxelDimensions_[2] * axialGeometry_.GetNormal());
+
+    Vector scaling;
+    
+    if (width_ == 0 ||
+        height_ == 0 ||
+        depth_ == 0)
+    {
+      LinearAlgebra::AssignVector(scaling, 1, 1, 1);
+    }
+    else
+    {
+      scaling = (
+        axialGeometry_.GetAxisX() * voxelDimensions_[0] * static_cast<double>(width_) +
+        axialGeometry_.GetAxisY() * voxelDimensions_[1] * static_cast<double>(height_) +
+        axialGeometry_.GetNormal() * voxelDimensions_[2] * static_cast<double>(depth_));
+    }
+
+    transform_ = LinearAlgebra::Product(
+      GeometryToolbox::CreateTranslationMatrix(origin[0], origin[1], origin[2]),
+      GeometryToolbox::CreateScalingMatrix(scaling[0], scaling[1], scaling[2]));
+
+    LinearAlgebra::InvertMatrix(transformInverse_, transform_);
+  }
+
+  
+  VolumeImageGeometry::VolumeImageGeometry() :
+    width_(0),
+    height_(0),
+    depth_(0)
+  {
+    LinearAlgebra::AssignVector(voxelDimensions_, 1, 1, 1);
+    Invalidate();
+  }
+
+
+  void VolumeImageGeometry::SetSize(unsigned int width,
+                                    unsigned int height,
+                                    unsigned int depth)
+  {
+    width_ = width;
+    height_ = height;
+    depth_ = depth;
+    Invalidate();
+  }
+
+  
+  void VolumeImageGeometry::SetAxialGeometry(const CoordinateSystem3D& geometry)
+  {
+    axialGeometry_ = geometry;
+    Invalidate();
+  }
+
+
+  void VolumeImageGeometry::SetVoxelDimensions(double x,
+                                               double y,
+                                               double z)
+  {
+    if (x <= 0 ||
+        y <= 0 ||
+        z <= 0)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+    else
+    {
+      LinearAlgebra::AssignVector(voxelDimensions_, x, y, z);
+      Invalidate();
+    }
+  }
+
+
+  const CoordinateSystem3D& VolumeImageGeometry::GetProjectionGeometry(VolumeProjection projection) const
+  {
+    switch (projection)
+    {
+      case VolumeProjection_Axial:
+        return axialGeometry_;
+
+      case VolumeProjection_Coronal:
+        return coronalGeometry_;
+
+      case VolumeProjection_Sagittal:
+        return sagittalGeometry_;
+
+      default:
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+  }
+
+  
+  Vector VolumeImageGeometry::GetVoxelDimensions(VolumeProjection projection) const
+  {
+    switch (projection)
+    {
+      case VolumeProjection_Axial:
+        return voxelDimensions_;
+
+      case VolumeProjection_Coronal:
+        return LinearAlgebra::CreateVector(voxelDimensions_[0], voxelDimensions_[2], voxelDimensions_[1]);
+
+      case VolumeProjection_Sagittal:
+        return LinearAlgebra::CreateVector(voxelDimensions_[1], voxelDimensions_[2], voxelDimensions_[0]);
+
+      default:
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+  }
+
+
+  unsigned int VolumeImageGeometry::GetProjectionWidth(VolumeProjection projection) const
+  {
+    switch (projection)
+    {
+      case VolumeProjection_Axial:
+        return width_;
+
+      case VolumeProjection_Coronal:
+        return width_;
+
+      case VolumeProjection_Sagittal:
+        return height_;
+
+      default:
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+  }
+
+
+  unsigned int VolumeImageGeometry::GetProjectionHeight(VolumeProjection projection) const
+  {
+    switch (projection)
+    {
+      case VolumeProjection_Axial:
+        return height_;
+
+      case VolumeProjection_Coronal:
+        return depth_;
+
+      case VolumeProjection_Sagittal:
+        return depth_;
+
+      default:
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+  }
+
+
+  unsigned int VolumeImageGeometry::GetProjectionDepth(VolumeProjection projection) const
+  {
+    switch (projection)
+    {
+      case VolumeProjection_Axial:
+        return depth_;
+
+      case VolumeProjection_Coronal:
+        return height_;
+
+      case VolumeProjection_Sagittal:
+        return width_;
+
+      default:
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }    
+  }
+
+
+  Vector VolumeImageGeometry::GetCoordinates(float x,
+                                             float y,
+                                             float z) const
+  {
+    Vector p = LinearAlgebra::Product(transform_, LinearAlgebra::CreateVector(x, y, z, 1));
+
+    assert(LinearAlgebra::IsNear(p[3], 1));  // Affine transform, no perspective effect
+
+    // Back to non-homogeneous coordinates
+    return LinearAlgebra::CreateVector(p[0], p[1], p[2]);
+  }
+
+
+  bool VolumeImageGeometry::DetectProjection(VolumeProjection& projection,
+                                             const Vector& planeNormal) const
+  {
+    if (GeometryToolbox::IsParallel(planeNormal, axialGeometry_.GetNormal()))
+    {
+      projection = VolumeProjection_Axial;
+      return true;
+    }
+    else if (GeometryToolbox::IsParallel(planeNormal, coronalGeometry_.GetNormal()))
+    {
+      projection = VolumeProjection_Coronal;
+      return true;
+    }
+    else if (GeometryToolbox::IsParallel(planeNormal, sagittalGeometry_.GetNormal()))
+    {
+      projection = VolumeProjection_Sagittal;
+      return true;
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+  
+  bool VolumeImageGeometry::DetectSlice(VolumeProjection& projection,
+                                        unsigned int& slice,
+                                        const CoordinateSystem3D& plane) const
+  {
+    if (!DetectProjection(projection, plane.GetNormal()))
+    {
+      return false;
+    }
+
+    // Transforms the coordinates of the origin of the plane, into the
+    // coordinates of the axial geometry
+    const Vector& origin = plane.GetOrigin();
+    Vector p = LinearAlgebra::Product(
+      transformInverse_,
+      LinearAlgebra::CreateVector(origin[0], origin[1], origin[2], 1));
+
+    assert(LinearAlgebra::IsNear(p[3], 1));
+
+    double z;
+
+    switch (projection)
+    {
+      case VolumeProjection_Axial:
+        z = p[2];
+        break;
+
+      case VolumeProjection_Coronal:
+        z = p[1];
+        break;
+
+      case VolumeProjection_Sagittal:
+        z = p[0];
+        break;
+
+      default:
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+
+    const unsigned int projectionDepth = GetProjectionDepth(projection);
+    
+    z *= static_cast<double>(projectionDepth);
+    if (z < 0)
+    {
+      return false;
+    }
+        
+    unsigned int d = static_cast<unsigned int>(std::floor(z));
+    if (d >= projectionDepth)
+    {
+      return false;
+    }
+    else
+    {
+      slice = d;
+      return true;
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Volumes/VolumeImageGeometry.h	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,122 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../StoneEnumerations.h"
+#include "../Toolbox/CoordinateSystem3D.h"
+
+namespace OrthancStone
+{
+  class VolumeImageGeometry
+  {
+  private:
+    unsigned int           width_;
+    unsigned int           height_;
+    unsigned int           depth_;
+    CoordinateSystem3D     axialGeometry_;
+    CoordinateSystem3D     coronalGeometry_;
+    CoordinateSystem3D     sagittalGeometry_;
+    Vector                 voxelDimensions_;
+    Matrix                 transform_;
+    Matrix                 transformInverse_;
+
+    void Invalidate();
+
+  public:
+    VolumeImageGeometry();
+
+    unsigned int GetWidth() const
+    {
+      return width_;
+    }
+
+    unsigned int GetHeight() const
+    {
+      return height_;
+    }
+
+    unsigned int GetDepth() const
+    {
+      return depth_;
+    }
+
+    const CoordinateSystem3D& GetAxialGeometry() const
+    {
+      return axialGeometry_;
+    }
+
+    const CoordinateSystem3D& GetCoronalGeometry() const
+    {
+      return coronalGeometry_;
+    }
+
+    const CoordinateSystem3D& GetSagittalGeometry() const
+    {
+      return sagittalGeometry_;
+    }
+
+    const CoordinateSystem3D& GetProjectionGeometry(VolumeProjection projection) const;
+    
+    const Matrix& GetTransform() const
+    {
+      return transform_;
+    }
+
+    const Matrix& GetTransformInverse() const
+    {
+      return transformInverse_;
+    }
+
+    void SetSize(unsigned int width,
+                 unsigned int height,
+                 unsigned int depth);
+
+    // Set the geometry of the first axial slice (i.e. the one whose
+    // depth == 0)
+    void SetAxialGeometry(const CoordinateSystem3D& geometry);
+
+    void SetVoxelDimensions(double x,
+                            double y,
+                            double z);
+
+    Vector GetVoxelDimensions(VolumeProjection projection) const;
+
+    unsigned int GetProjectionWidth(VolumeProjection projection) const;
+
+    unsigned int GetProjectionHeight(VolumeProjection projection) const;
+
+    unsigned int GetProjectionDepth(VolumeProjection projection) const;
+
+    // Get the 3D position of a point in the volume, where x, y and z
+    // lie in the [0;1] range
+    Vector GetCoordinates(float x,
+                          float y,
+                          float z) const;
+
+    bool DetectProjection(VolumeProjection& projection,
+                          const Vector& planeNormal) const;
+
+    bool DetectSlice(VolumeProjection& projection,
+                     unsigned int& slice,
+                     const CoordinateSystem3D& plane) const;
+  };
+}
--- a/Framework/Volumes/VolumeReslicer.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Volumes/VolumeReslicer.cpp	Wed May 22 16:13:46 2019 +0200
@@ -230,7 +230,7 @@
       FastRowIterator(const Orthanc::ImageAccessor& slice,
                       const Extent2D& extent,
                       const CoordinateSystem3D& plane,
-                      const OrientedBoundingBox& box,
+                      const OrientedVolumeBoundingBox& box,
                       unsigned int y)
       {
         const double width = static_cast<double>(slice.GetWidth());
@@ -285,7 +285,7 @@
       const Orthanc::ImageAccessor&  slice_;
       const Extent2D&                extent_;
       const CoordinateSystem3D&      plane_;
-      const OrientedBoundingBox&     box_;
+      const OrientedVolumeBoundingBox&     box_;
       unsigned int                   x_;
       unsigned int                   y_;
       
@@ -293,7 +293,7 @@
       SlowRowIterator(const Orthanc::ImageAccessor& slice,
                       const Extent2D& extent,
                       const CoordinateSystem3D& plane,
-                      const OrientedBoundingBox& box,
+                      const OrientedVolumeBoundingBox& box,
                       unsigned int y) :
         slice_(slice),
         extent_(extent),
@@ -342,7 +342,7 @@
                              const Extent2D& extent,
                              const ImageBuffer3D& source,
                              const CoordinateSystem3D& plane,
-                             const OrientedBoundingBox& box,
+                             const OrientedVolumeBoundingBox& box,
                              float scaling,
                              float offset)
     {
@@ -386,7 +386,7 @@
                              const Extent2D& extent,
                              const ImageBuffer3D& source,
                              const CoordinateSystem3D& plane,
-                             const OrientedBoundingBox& box,
+                             const OrientedVolumeBoundingBox& box,
                              ImageInterpolation interpolation,
                              bool hasLinearFunction,
                              float scaling,
@@ -452,7 +452,7 @@
                              const Extent2D& extent,
                              const ImageBuffer3D& source,
                              const CoordinateSystem3D& plane,
-                             const OrientedBoundingBox& box,
+                             const OrientedVolumeBoundingBox& box,
                              ImageInterpolation interpolation,
                              bool hasLinearFunction,
                              float scaling,
@@ -501,7 +501,7 @@
 
   void VolumeReslicer::CheckIterators(const ImageBuffer3D& source,
                                       const CoordinateSystem3D& plane,
-                                      const OrientedBoundingBox& box) const
+                                      const OrientedVolumeBoundingBox& box) const
   {
     for (unsigned int y = 0; y < slice_->GetHeight(); y++)
     {
@@ -745,12 +745,13 @@
 
   
   void VolumeReslicer::Apply(const ImageBuffer3D& source,
+                             const VolumeImageGeometry& geometry,
                              const CoordinateSystem3D& plane)
   {
     // Choose the default voxel size as the finest voxel dimension
     // of the source volumetric image
     const OrthancStone::Vector dim =
-      source.GetGeometry().GetVoxelDimensions(OrthancStone::VolumeProjection_Axial);
+      geometry.GetVoxelDimensions(OrthancStone::VolumeProjection_Axial);
     double voxelSize = dim[0];
     
     if (dim[1] < voxelSize)
@@ -768,11 +769,12 @@
       throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
     }
 
-    Apply(source, plane, voxelSize);
+    Apply(source, geometry, plane, voxelSize);
   }
 
   
   void VolumeReslicer::Apply(const ImageBuffer3D& source,
+                             const VolumeImageGeometry& geometry,
                              const CoordinateSystem3D& plane,
                              double voxelSize)
   {
@@ -783,7 +785,7 @@
     // to 6 vertices. We compute the extent of the intersection
     // polygon, with respect to the coordinate system of the reslicing
     // plane.
-    OrientedBoundingBox box(source);
+    OrientedVolumeBoundingBox box(geometry);
 
     if (!box.ComputeExtent(extent_, plane))
     {
--- a/Framework/Volumes/VolumeReslicer.h	Wed May 22 16:01:34 2019 +0200
+++ b/Framework/Volumes/VolumeReslicer.h	Wed May 22 16:13:46 2019 +0200
@@ -22,7 +22,7 @@
 #pragma once
 
 #include "../Toolbox/Extent2D.h"
-#include "../Toolbox/OrientedBoundingBox.h"
+#include "OrientedVolumeBoundingBox.h"
 #include "ImageBuffer3D.h"
 
 namespace OrthancStone
@@ -46,7 +46,7 @@
 
     void CheckIterators(const ImageBuffer3D& source,
                         const CoordinateSystem3D& plane,
-                        const OrientedBoundingBox& box) const;
+                        const OrientedVolumeBoundingBox& box) const;
 
     void Reset();
 
@@ -111,9 +111,11 @@
     Orthanc::ImageAccessor* ReleaseOutputSlice();
 
     void Apply(const ImageBuffer3D& source,
+               const VolumeImageGeometry& geometry,
                const CoordinateSystem3D& plane);
 
     void Apply(const ImageBuffer3D& source,
+               const VolumeImageGeometry& geometry,
                const CoordinateSystem3D& plane,
                double voxelSize);
   };
--- a/Framework/Widgets/CairoWidget.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,101 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "CairoWidget.h"
-
-#include <Core/Images/ImageProcessing.h>
-#include <Core/OrthancException.h>
-
-namespace OrthancStone
-{
-  static bool IsAligned(const Orthanc::ImageAccessor& target)
-  {
-    // TODO
-    return true;
-  }
-
-  CairoWidget::CairoWidget(const std::string& name) :
-    WidgetBase(name)
-  {
-  }
-
-  void CairoWidget::SetSize(unsigned int width,
-                            unsigned int height)
-  {
-    surface_.SetSize(width, height, false /* no alpha */);
-  }
-  
-
-  bool CairoWidget::Render(Orthanc::ImageAccessor& target)
-  {
-    // Don't call the base class here, as
-    // "ClearBackgroundCairo()" is a faster alternative
-
-    if (IsAligned(target))
-    {
-      CairoSurface surface(target, false /* no alpha */);
-      CairoContext context(surface);
-      ClearBackgroundCairo(context);
-      return RenderCairo(context);
-    }
-    else
-    {
-      CairoContext context(surface_);
-      ClearBackgroundCairo(context);
-
-      if (RenderCairo(context))
-      {
-        Orthanc::ImageAccessor surface;
-        surface_.GetReadOnlyAccessor(surface);
-        Orthanc::ImageProcessing::Copy(target, surface);
-        return true;
-      }
-      else
-      {
-        return false;
-      }
-    }
-  }
-
-
-  void CairoWidget::RenderMouseOver(Orthanc::ImageAccessor& target,
-                                    int x,
-                                    int y)
-  {
-    if (IsAligned(target))
-    {
-      CairoSurface surface(target, false /* no alpha */);
-      CairoContext context(surface);
-      RenderMouseOverCairo(context, x, y);
-    }
-    else
-    {
-      Orthanc::ImageAccessor accessor;
-      surface_.GetWriteableAccessor(accessor);
-      Orthanc::ImageProcessing::Copy(accessor, target);
-
-      CairoContext context(surface_);
-      RenderMouseOverCairo(context, x, y);
-
-      Orthanc::ImageProcessing::Copy(target, accessor);
-    }
-  }
-}
--- a/Framework/Widgets/CairoWidget.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "WidgetBase.h"
-
-namespace OrthancStone
-{
-  class CairoWidget : public WidgetBase
-  {
-  private:
-    CairoSurface   surface_;
-
-  protected:
-    virtual bool RenderCairo(CairoContext& context) = 0;
-    
-    virtual void RenderMouseOverCairo(CairoContext& context,
-                                      int x,
-                                      int y) = 0;
-    
-  public:
-    CairoWidget(const std::string& name);
-
-    virtual void SetSize(unsigned int width,
-                         unsigned int height);
-
-    virtual bool Render(Orthanc::ImageAccessor& target);
-
-    virtual void RenderMouseOver(Orthanc::ImageAccessor& target,
-                                 int x,
-                                 int y);  
-  };
-}
--- a/Framework/Widgets/EmptyWidget.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "EmptyWidget.h"
-
-#include <Core/Images/ImageProcessing.h>
-#include <Core/OrthancException.h>
-
-namespace OrthancStone
-{
-  bool EmptyWidget::Render(Orthanc::ImageAccessor& surface)
-  {
-    // Note: This call is slow
-    Orthanc::ImageProcessing::Set(surface, red_, green_, blue_, 255);
-    return true;
-  }
-
-
-  void EmptyWidget::DoAnimation()
-  {
-    throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-  }
-}
--- a/Framework/Widgets/EmptyWidget.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,116 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "IWidget.h"
-
-namespace OrthancStone
-{
-  /**
-   * This is a test widget that simply fills its surface with an
-   * uniform color.
-   **/
-  class EmptyWidget : public IWidget
-  {
-  private:
-    uint8_t  red_;
-    uint8_t  green_;
-    uint8_t  blue_;
-
-  public:
-    EmptyWidget(uint8_t red,
-                uint8_t green,
-                uint8_t blue) :
-      red_(red),
-      green_(green),
-      blue_(blue)
-    {
-    }
-
-    virtual void FitContent()
-    {
-    }
-
-    virtual void SetParent(IWidget& widget)
-    {
-    }
-
-    virtual void SetViewport(WidgetViewport& viewport)
-    {
-    }
-
-    virtual void NotifyContentChanged()
-    {
-    }
-
-    virtual void SetStatusBar(IStatusBar& statusBar)
-    {
-    }
-
-    virtual void SetSize(unsigned int width,
-                         unsigned int height)
-    {
-    }
-
-    virtual bool Render(Orthanc::ImageAccessor& surface);
-
-    virtual IMouseTracker* CreateMouseTracker(MouseButton button,
-                                              int x,
-                                              int y,
-                                              KeyboardModifiers modifiers,
-                                              const std::vector<Touch>& touches)
-    {
-      return NULL;
-    }
-
-    virtual void RenderMouseOver(Orthanc::ImageAccessor& target,
-                                 int x,
-                                 int y)
-    {
-    }
-
-    virtual void MouseWheel(MouseWheelDirection direction,
-                            int x,
-                            int y,
-                            KeyboardModifiers modifiers)
-    {
-    }
-
-    virtual void KeyPressed(KeyboardKeys key,
-                            char keyChar,
-                            KeyboardModifiers modifiers)
-    {
-    }
-
-    virtual bool HasAnimation() const
-    {
-      return false;
-    }
-
-    virtual void DoAnimation();
-
-    virtual bool HasRenderMouseOver()
-    {
-      return false;
-    }
-  };
-}
--- a/Framework/Widgets/IWidget.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,81 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "../StoneEnumerations.h"
-#include "../Viewport/IMouseTracker.h"
-#include "../Viewport/IStatusBar.h"
-
-namespace OrthancStone
-{
-  class WidgetViewport;  // Forward declaration
-  
-  class IWidget : public boost::noncopyable
-  {
-  public:
-    virtual ~IWidget()
-    {
-    }
-
-    virtual void FitContent() = 0;
-
-    virtual void SetParent(IWidget& parent) = 0;
-    
-    virtual void SetViewport(WidgetViewport& viewport) = 0;
-
-    virtual void SetStatusBar(IStatusBar& statusBar) = 0;
-
-    virtual void SetSize(unsigned int width, 
-                         unsigned int height) = 0;
- 
-    virtual bool Render(Orthanc::ImageAccessor& surface) = 0;
-
-    virtual IMouseTracker* CreateMouseTracker(MouseButton button,
-                                              int x,
-                                              int y,
-                                              KeyboardModifiers modifiers,
-                                              const std::vector<Touch>& touches) = 0;
-
-    virtual void RenderMouseOver(Orthanc::ImageAccessor& target,
-                                 int x,
-                                 int y) = 0;
-
-    virtual bool HasRenderMouseOver() = 0;
-
-    virtual void MouseWheel(MouseWheelDirection direction,
-                            int x,
-                            int y,
-                            KeyboardModifiers modifiers) = 0;
-
-    virtual void KeyPressed(KeyboardKeys key,
-                            char keyChar,
-                            KeyboardModifiers modifiers) = 0;
-
-    virtual bool HasAnimation() const = 0;
-
-    virtual void DoAnimation() = 0;
-
-    // Subclasses can call this method to signal the display of the
-    // widget must be refreshed
-    virtual void NotifyContentChanged() = 0;
-  };
-}
--- a/Framework/Widgets/IWorldSceneInteractor.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,70 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "IWorldSceneMouseTracker.h"
-
-#include "../Toolbox/ViewportGeometry.h"
-#include "../StoneEnumerations.h"
-#include "../Viewport/IStatusBar.h"
-
-namespace OrthancStone
-{
-    class WorldSceneWidget;
-
-    class IWorldSceneInteractor : public boost::noncopyable
-    {
-    public:
-        virtual ~IWorldSceneInteractor()
-        {
-        }
-
-        virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget,
-                                                            const ViewportGeometry& view,
-                                                            MouseButton button,
-                                                            KeyboardModifiers modifiers,
-                                                            int viewportX,
-                                                            int viewportY,
-                                                            double x,
-                                                            double y,
-                                                            IStatusBar* statusBar,
-                                                            const std::vector<Touch>& touches) = 0;
-
-        virtual void MouseOver(CairoContext& context,
-                               WorldSceneWidget& widget,
-                               const ViewportGeometry& view,
-                               double x,
-                               double y,
-                               IStatusBar* statusBar) = 0;
-
-        virtual void MouseWheel(WorldSceneWidget& widget,
-                                MouseWheelDirection direction,
-                                KeyboardModifiers modifiers,
-                                IStatusBar* statusBar) = 0;
-
-        virtual void KeyPressed(WorldSceneWidget& widget,
-                                KeyboardKeys key,
-                                char keyChar,
-                                KeyboardModifiers modifiers,
-                                IStatusBar* statusBar) = 0;
-    };
-}
--- a/Framework/Widgets/IWorldSceneMouseTracker.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "../Viewport/CairoContext.h"
-#include "../Viewport/IMouseTracker.h" // only to get the "Touch" definition
-
-namespace OrthancStone
-{
-
-  // this is tracking a mouse in scene coordinates/mm unlike
-  // the IMouseTracker that is tracking a mouse
-  // in screen coordinates/pixels.
-  class IWorldSceneMouseTracker : public boost::noncopyable
-  {
-  public:
-    virtual ~IWorldSceneMouseTracker()
-    {
-    }
-
-    virtual bool HasRender() const = 0;
-
-    virtual void Render(CairoContext& context,
-                        double zoom) = 0;
-
-    virtual void MouseUp() = 0;
-
-    virtual void MouseMove(int displayX,
-                           int displayY,
-                           double sceneX,
-                           double sceneY,
-                           const std::vector<Touch>& displayTouches,
-                           const std::vector<Touch>& sceneTouches) = 0;
-  };
-}
--- a/Framework/Widgets/LayoutWidget.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,503 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "LayoutWidget.h"
-
-#include <Core/Logging.h>
-#include <Core/OrthancException.h>
-
-#include <boost/math/special_functions/round.hpp>
-
-namespace OrthancStone
-{
-  class LayoutWidget::LayoutMouseTracker : public IMouseTracker
-  {
-  private:
-    std::auto_ptr<IMouseTracker>   tracker_;
-    int                            left_;
-    int                            top_;
-    unsigned int                   width_;
-    unsigned int                   height_;
-
-  public:
-    LayoutMouseTracker(IMouseTracker* tracker,
-                       int left,
-                       int top,
-                       unsigned int width,
-                       unsigned int height) :
-      tracker_(tracker),
-      left_(left),
-      top_(top),
-      width_(width),
-      height_(height)
-    {
-      if (tracker == NULL)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-      }
-    }
-
-    virtual void Render(Orthanc::ImageAccessor& surface)
-    {
-      Orthanc::ImageAccessor accessor;
-      surface.GetRegion(accessor, left_, top_, width_, height_);
-      tracker_->Render(accessor);
-    }
-
-    virtual void MouseUp()
-    {
-      tracker_->MouseUp();
-    }
-
-    virtual void MouseMove(int x, 
-                           int y,
-                           const std::vector<Touch>& displayTouches)
-    {
-      std::vector<Touch> relativeTouches;
-      for (size_t t = 0; t < displayTouches.size(); t++)
-      {
-        relativeTouches.push_back(Touch(displayTouches[t].x - left_, displayTouches[t].y - top_));
-      }
-
-      tracker_->MouseMove(x - left_, y - top_, relativeTouches);
-    }
-  };
-
-
-  class LayoutWidget::ChildWidget : public boost::noncopyable
-  {
-  private:
-    std::auto_ptr<IWidget>  widget_;
-    int                     left_;
-    int                     top_;
-    unsigned int            width_;
-    unsigned int            height_;
-
-  public:
-    ChildWidget(IWidget* widget) :
-      widget_(widget)
-    {
-      assert(widget != NULL);
-      SetEmpty();
-    }
-
-    void DoAnimation()
-    {
-      if (widget_->HasAnimation())
-      {
-        widget_->DoAnimation();
-      }
-    }
-
-    IWidget& GetWidget() const
-    {
-      return *widget_;
-    }
-
-    void SetRectangle(unsigned int left, 
-                      unsigned int top,
-                      unsigned int width,
-                      unsigned int height)
-    {
-      left_ = left;
-      top_ = top;
-      width_ = width;
-      height_ = height;
-
-      widget_->SetSize(width, height);
-    }
-
-    void SetEmpty()
-    {
-      SetRectangle(0, 0, 0, 0);
-    }
-
-    bool Contains(int x, 
-                  int y) const
-    {
-      return (x >= left_ && 
-              y >= top_ &&
-              x < left_ + static_cast<int>(width_) &&
-              y < top_ + static_cast<int>(height_));
-    }      
-
-    bool Render(Orthanc::ImageAccessor& target)
-    {
-      if (width_ == 0 ||
-          height_ == 0)
-      {
-        return true;
-      }
-      else 
-      {
-        Orthanc::ImageAccessor accessor;
-        target.GetRegion(accessor, left_, top_, width_, height_);
-        return widget_->Render(accessor);
-      }
-    }
-
-    IMouseTracker* CreateMouseTracker(MouseButton button,
-                                      int x,
-                                      int y,
-                                      KeyboardModifiers modifiers,
-                                      const std::vector<Touch>& touches)
-    {
-      if (Contains(x, y))
-      {
-        IMouseTracker* tracker = widget_->CreateMouseTracker(button, 
-                                                             x - left_, 
-                                                             y - top_, 
-                                                             modifiers,
-                                                             touches);
-        if (tracker)
-        {
-          return new LayoutMouseTracker(tracker, left_, top_, width_, height_);
-        }
-      }
-
-      return NULL;
-    }
-
-    void RenderMouseOver(Orthanc::ImageAccessor& target,
-                         int x,
-                         int y)
-    {
-      if (Contains(x, y))
-      {
-        Orthanc::ImageAccessor accessor;
-        target.GetRegion(accessor, left_, top_, width_, height_);
-
-        widget_->RenderMouseOver(accessor, x - left_, y - top_);
-      }
-    }
-
-    void MouseWheel(MouseWheelDirection direction,
-                    int x,
-                    int y,
-                    KeyboardModifiers modifiers)
-    {
-      if (Contains(x, y))
-      {
-        widget_->MouseWheel(direction, x - left_, y - top_, modifiers);
-      }
-    }
-    
-    bool HasRenderMouseOver()
-    {
-      return widget_->HasRenderMouseOver();
-    }
-  };
-
-
-  void LayoutWidget::ComputeChildrenExtents()
-  {
-    if (children_.size() == 0)
-    {
-      return;
-    }
-
-    float internal = static_cast<float>(paddingInternal_);
-
-    if (width_ <= paddingLeft_ + paddingRight_ ||
-        height_ <= paddingTop_ + paddingBottom_)
-    {
-      for (size_t i = 0; i < children_.size(); i++)
-      {
-        children_[i]->SetEmpty();          
-      }
-    }
-    else if (isHorizontal_)
-    {
-      unsigned int padding = paddingLeft_ + paddingRight_ + (static_cast<unsigned int>(children_.size()) - 1) * paddingInternal_;
-      float childWidth = ((static_cast<float>(width_) - static_cast<float>(padding)) / 
-                          static_cast<float>(children_.size()));
-        
-      for (size_t i = 0; i < children_.size(); i++)
-      {
-        float left = static_cast<float>(paddingLeft_) + static_cast<float>(i) * (childWidth + internal);
-        float right = left + childWidth;
-
-        if (left >= right)
-        {
-          children_[i]->SetEmpty();
-        }
-        else
-        {
-          children_[i]->SetRectangle(static_cast<unsigned int>(left), 
-                                     paddingTop_, 
-                                     boost::math::iround(right - left),
-                                     height_ - paddingTop_ - paddingBottom_);
-        }
-      }
-    }
-    else
-    {
-      unsigned int padding = paddingTop_ + paddingBottom_ + (static_cast<unsigned int>(children_.size()) - 1) * paddingInternal_;
-      float childHeight = ((static_cast<float>(height_) - static_cast<float>(padding)) / 
-                           static_cast<float>(children_.size()));
-        
-      for (size_t i = 0; i < children_.size(); i++)
-      {
-        float top = static_cast<float>(paddingTop_) + static_cast<float>(i) * (childHeight + internal);
-        float bottom = top + childHeight;
-
-        if (top >= bottom)
-        {
-          children_[i]->SetEmpty();
-        }
-        else
-        {
-          children_[i]->SetRectangle(paddingTop_, 
-                                     static_cast<unsigned int>(top), 
-                                     width_ - paddingLeft_ - paddingRight_,
-                                     boost::math::iround(bottom - top));
-        }
-      }
-    }
-
-    NotifyContentChanged(*this);
-  }
-
-
-  LayoutWidget::LayoutWidget(const std::string& name) :
-    WidgetBase(name),
-    isHorizontal_(true),
-    width_(0),
-    height_(0),
-    paddingLeft_(0),
-    paddingTop_(0),
-    paddingRight_(0),
-    paddingBottom_(0),
-    paddingInternal_(0)
-  {
-  }
-
-
-  LayoutWidget::~LayoutWidget()
-  {
-    for (size_t i = 0; i < children_.size(); i++)
-    {
-      delete children_[i];
-    }
-  }
-
-
-  void LayoutWidget::FitContent()
-  {
-    for (size_t i = 0; i < children_.size(); i++)
-    {
-      children_[i]->GetWidget().FitContent();
-    }
-  }
-  
-
-  void LayoutWidget::NotifyContentChanged(const IWidget& widget)
-  {
-    // One of the children has changed
-    WidgetBase::NotifyContentChanged();
-  }
-
-
-  void LayoutWidget::SetHorizontal()
-  {
-    isHorizontal_ = true;
-    ComputeChildrenExtents();
-  }
-
-
-  void LayoutWidget::SetVertical()
-  {
-    isHorizontal_ = false;
-    ComputeChildrenExtents();
-  }
-
-
-  void LayoutWidget::SetPadding(unsigned int left,
-                                unsigned int top,
-                                unsigned int right,
-                                unsigned int bottom,
-                                unsigned int spacing)
-  {
-    paddingLeft_ = left;
-    paddingTop_ = top;
-    paddingRight_ = right;
-    paddingBottom_ = bottom;
-    paddingInternal_ = spacing;
-  }
-    
-
-  void LayoutWidget::SetPadding(unsigned int padding)
-  {
-    paddingLeft_ = padding;
-    paddingTop_ = padding;
-    paddingRight_ = padding;
-    paddingBottom_ = padding;
-    paddingInternal_ = padding;
-  }
-
-
-  IWidget& LayoutWidget::AddWidget(IWidget* widget)  // Takes ownership
-  {
-    if (widget == NULL)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-
-    if (GetStatusBar() != NULL)
-    {
-      widget->SetStatusBar(*GetStatusBar());
-    }
-
-    children_.push_back(new ChildWidget(widget));
-    widget->SetParent(*this);
-
-    ComputeChildrenExtents();
-
-    if (widget->HasAnimation())
-    {
-      hasAnimation_ = true;
-    }
-
-    return *widget;
-  }
-
-
-  void LayoutWidget::SetStatusBar(IStatusBar& statusBar)
-  {
-    WidgetBase::SetStatusBar(statusBar);
-
-    for (size_t i = 0; i < children_.size(); i++)
-    {
-      children_[i]->GetWidget().SetStatusBar(statusBar);
-    }
-  }
-
-
-  void LayoutWidget::SetSize(unsigned int width,
-                             unsigned int height)
-  {
-    width_ = width;
-    height_ = height;
-    ComputeChildrenExtents();
-  }
-
-
-  bool LayoutWidget::Render(Orthanc::ImageAccessor& surface)
-  {
-    if (!WidgetBase::Render(surface))
-    {
-      return false;
-    }
-
-    for (size_t i = 0; i < children_.size(); i++)
-    {
-      if (!children_[i]->Render(surface))
-      {
-        return false;
-      }
-    }
-
-    return true;
-  }
-
-    
-  IMouseTracker* LayoutWidget::CreateMouseTracker(MouseButton button,
-                                                  int x,
-                                                  int y,
-                                                  KeyboardModifiers modifiers,
-                                                  const std::vector<Touch>& touches)
-  {
-    for (size_t i = 0; i < children_.size(); i++)
-    {
-      IMouseTracker* tracker = children_[i]->CreateMouseTracker(button, x, y, modifiers, touches);
-      if (tracker != NULL)
-      {
-        return tracker;
-      }
-    }
-
-    return NULL;
-  }
-
-
-  void LayoutWidget::RenderMouseOver(Orthanc::ImageAccessor& target,
-                                     int x,
-                                     int y)
-  {
-    for (size_t i = 0; i < children_.size(); i++)
-    {
-      children_[i]->RenderMouseOver(target, x, y);
-    }
-  }
-
-
-  void LayoutWidget::MouseWheel(MouseWheelDirection direction,
-                                int x,
-                                int y,
-                                KeyboardModifiers modifiers)
-  {
-    for (size_t i = 0; i < children_.size(); i++)
-    {
-      children_[i]->MouseWheel(direction, x, y, modifiers);
-    }
-  }
-
-
-  void LayoutWidget::KeyPressed(KeyboardKeys key,
-                                char keyChar,
-                                KeyboardModifiers modifiers)
-  {
-    for (size_t i = 0; i < children_.size(); i++)
-    {
-      children_[i]->GetWidget().KeyPressed(key, keyChar, modifiers);
-    }
-  }
-
-  
-  void LayoutWidget::DoAnimation()
-  {
-    if (hasAnimation_)
-    {
-      for (size_t i = 0; i < children_.size(); i++)
-      {
-        children_[i]->DoAnimation();
-      }
-    }
-    else
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-    }
-  }
-
-
-  bool LayoutWidget::HasRenderMouseOver()
-  {
-    for (size_t i = 0; i < children_.size(); i++)
-    {
-      if (children_[i]->HasRenderMouseOver())
-      {
-        return true;
-      }
-    }
-
-    return false;
-  }
-}
--- a/Framework/Widgets/LayoutWidget.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,134 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "WidgetBase.h"
-
-#include <vector>
-#include <memory>
-
-namespace OrthancStone
-{
-  class LayoutWidget : public WidgetBase
-  {
-  private:
-    class LayoutMouseTracker;
-    class ChildWidget;
-
-    std::vector<ChildWidget*>     children_;
-    bool                          isHorizontal_;
-    unsigned int                  width_;
-    unsigned int                  height_;
-    std::auto_ptr<IMouseTracker>  mouseTracker_;
-    unsigned int                  paddingLeft_;
-    unsigned int                  paddingTop_;
-    unsigned int                  paddingRight_;
-    unsigned int                  paddingBottom_;
-    unsigned int                  paddingInternal_;
-    bool                          hasAnimation_;
-
-    void ComputeChildrenExtents();
-
-  public:
-    LayoutWidget(const std::string& name);
-
-    virtual ~LayoutWidget();
-
-    virtual void FitContent();
-
-    virtual void NotifyContentChanged(const IWidget& widget);
-
-    void SetHorizontal();
-
-    void SetVertical();
-
-    void SetPadding(unsigned int left,
-                    unsigned int top,
-                    unsigned int right,
-                    unsigned int bottom,
-                    unsigned int spacing);
-    
-    void SetPadding(unsigned int padding);
-
-    unsigned int GetPaddingLeft() const
-    {
-      return paddingLeft_;
-    }
-
-    unsigned int GetPaddingTop() const
-    {
-      return paddingTop_;
-    }
-
-    unsigned int GetPaddingRight() const
-    {
-      return paddingRight_;
-    }
-
-    unsigned int GetPaddingBottom() const
-    {
-      return paddingBottom_;
-    }
-
-    unsigned int GetPaddingInternal() const
-    {
-      return paddingInternal_;
-    }
-
-    IWidget& AddWidget(IWidget* widget);  // Takes ownership
-
-    virtual void SetStatusBar(IStatusBar& statusBar);
-
-    virtual void SetSize(unsigned int width,
-                         unsigned int height);
-
-    virtual bool Render(Orthanc::ImageAccessor& surface);
-    
-    virtual IMouseTracker* CreateMouseTracker(MouseButton button,
-                                              int x,
-                                              int y,
-                                              KeyboardModifiers modifiers,
-                                              const std::vector<Touch>& touches);
-
-    virtual void RenderMouseOver(Orthanc::ImageAccessor& target,
-                                 int x,
-                                 int y);
-
-    virtual void MouseWheel(MouseWheelDirection direction,
-                            int x,
-                            int y,
-                            KeyboardModifiers modifiers);
-
-    virtual void KeyPressed(KeyboardKeys key,
-                            char keyChar,
-                            KeyboardModifiers modifiers);
-
-    virtual bool HasAnimation() const
-    {
-      return hasAnimation_;
-    }
-
-    virtual void DoAnimation();
-
-    virtual bool HasRenderMouseOver();
-  };
-}
--- a/Framework/Widgets/PanMouseTracker.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "PanMouseTracker.h"
-
-#include <Core/Logging.h>
-#include <Core/OrthancException.h>
-
-namespace OrthancStone
-{
-  PanMouseTracker::PanMouseTracker(WorldSceneWidget& that,
-                                   int x,
-                                   int y) :
-    that_(that)
-  {
-    that.GetView().GetPan(originalPanX_, originalPanY_);
-    that.GetView().MapPixelCenterToScene(downX_, downY_, x, y);
-  }
-    
-
-  void PanMouseTracker::Render(CairoContext& context,
-                               double zoom)
-  {
-    throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-  }
-
-
-  void PanMouseTracker::MouseMove(int displayX,
-                                  int displayY,
-                                  double x,
-                                  double y,
-                                  const std::vector<Touch>& displayTouches,
-                                  const std::vector<Touch>& sceneTouches)
-  {
-    ViewportGeometry view = that_.GetView();
-    view.SetPan(originalPanX_ + (x - downX_) * view.GetZoom(),
-                originalPanY_ + (y - downY_) * view.GetZoom());
-    that_.SetView(view);
-  }
-}
--- a/Framework/Widgets/PanMouseTracker.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "WorldSceneWidget.h"
-
-namespace OrthancStone
-{
-  class PanMouseTracker : public IWorldSceneMouseTracker
-  {
-  private:
-    WorldSceneWidget&  that_;
-    double             originalPanX_;
-    double             originalPanY_;
-    double             downX_;
-    double             downY_;
-    
-  public:
-    PanMouseTracker(WorldSceneWidget& that,
-                    int x,
-                    int y);
-    
-    virtual bool HasRender() const
-    {
-      return false;
-    }
-
-    virtual void MouseUp()
-    {
-    }
-
-    virtual void Render(CairoContext& context,
-                        double zoom);
-
-    virtual void MouseMove(int displayX,
-                           int displayY,
-                           double x,
-                           double y,
-                           const std::vector<Touch>& displayTouches,
-                           const std::vector<Touch>& sceneTouches);
-  };
-}
--- a/Framework/Widgets/PanZoomMouseTracker.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,137 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "PanZoomMouseTracker.h"
-
-#include <Core/Logging.h>
-#include <Core/OrthancException.h>
-#include <math.h>
-
-namespace OrthancStone
-{
-  Touch GetCenter(const std::vector<Touch>& touches)
-  {
-    return Touch((touches[0].x + touches[1].x) / 2.0f, (touches[0].y + touches[1].y) / 2.0f);
-  }
-
-  double GetDistance(const std::vector<Touch>& touches)
-  {
-    float dx = touches[0].x - touches[1].x;
-    float dy = touches[0].y - touches[1].y;
-    return sqrt((double)(dx * dx) + (double)(dy * dy));
-  }
-
-
-  PanZoomMouseTracker::PanZoomMouseTracker(WorldSceneWidget& that,
-                                           const std::vector<Touch>& startTouches)
-    : that_(that),
-      originalZoom_(that.GetView().GetZoom())
-  {
-    that.GetView().GetPan(originalPanX_, originalPanY_);
-    that.GetView().MapPixelCenterToScene(originalSceneTouches_, startTouches);
-
-    originalDisplayCenter_ = GetCenter(startTouches);
-    originalSceneCenter_ = GetCenter(originalSceneTouches_);
-    originalDisplayDistanceBetweenTouches_ = GetDistance(startTouches);
-
-//    printf("original Pan %f %f\n", originalPanX_, originalPanY_);
-//    printf("original Zoom %f \n", originalZoom_);
-//    printf("original distance %f \n", (float)originalDisplayDistanceBetweenTouches_);
-//    printf("original display touches 0 %f %f\n", startTouches[0].x, startTouches[0].y);
-//    printf("original display touches 1 %f %f\n", startTouches[1].x, startTouches[1].y);
-//    printf("original Scene center %f %f\n", originalSceneCenter_.x, originalSceneCenter_.y);
-
-    unsigned int height = that.GetView().GetDisplayHeight();
-
-    if (height <= 3)
-    {
-      idle_ = true;
-      LOG(WARNING) << "image is too small to zoom (current height = " << height << ")";
-    }
-    else
-    {
-      idle_ = false;
-      normalization_ = 1.0 / static_cast<double>(height - 1);
-    }
-
-  }
-
-
-  void PanZoomMouseTracker::Render(CairoContext& context,
-                                   double zoom)
-  {
-    throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-  }
-
-
-  void PanZoomMouseTracker::MouseMove(int displayX,
-                                      int displayY,
-                                      double x,
-                                      double y,
-                                      const std::vector<Touch>& displayTouches,
-                                      const std::vector<Touch>& sceneTouches)
-  {
-    ViewportGeometry view = that_.GetView();
-
-//    printf("Display touches 0 %f %f\n", displayTouches[0].x, displayTouches[0].y);
-//    printf("Display touches 1 %f %f\n", displayTouches[1].x, displayTouches[1].y);
-//    printf("Scene touches 0 %f %f\n", sceneTouches[0].x, sceneTouches[0].y);
-//    printf("Scene touches 1 %f %f\n", sceneTouches[1].x, sceneTouches[1].y);
-
-//    printf("zoom = %f\n", view.GetZoom());
-    Touch currentSceneCenter = GetCenter(sceneTouches);
-    double panX = originalPanX_ + (currentSceneCenter.x - originalSceneCenter_.x) * view.GetZoom();
-    double panY = originalPanY_ + (currentSceneCenter.y - originalSceneCenter_.y) * view.GetZoom();
-
-    view.SetPan(panX, panY);
-
-    static const double MIN_ZOOM = -4;
-    static const double MAX_ZOOM = 4;
-
-    if (!idle_)
-    {
-      double currentDistanceBetweenTouches = GetDistance(displayTouches);
-
-      double dy = static_cast<double>(currentDistanceBetweenTouches - originalDisplayDistanceBetweenTouches_) * normalization_;  // In the range [-1,1]
-      double z;
-
-      // Linear interpolation from [-1, 1] to [MIN_ZOOM, MAX_ZOOM]
-      if (dy < -1.0)
-      {
-        z = MIN_ZOOM;
-      }
-      else if (dy > 1.0)
-      {
-        z = MAX_ZOOM;
-      }
-      else
-      {
-        z = MIN_ZOOM + (MAX_ZOOM - MIN_ZOOM) * (dy + 1.0) / 2.0;
-      }
-
-      z = pow(2.0, z);
-
-      view.SetZoom(z * originalZoom_);
-    }
-
-    that_.SetView(view);
-  }
-}
--- a/Framework/Widgets/PanZoomMouseTracker.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,65 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "WorldSceneWidget.h"
-
-namespace OrthancStone
-{
-  class PanZoomMouseTracker : public IWorldSceneMouseTracker
-  {
-  private:
-    WorldSceneWidget&  that_;
-    std::vector<Touch> originalSceneTouches_;
-    Touch              originalSceneCenter_;
-    Touch              originalDisplayCenter_;
-    double             originalPanX_;
-    double             originalPanY_;
-    double             originalZoom_;
-    double             originalDisplayDistanceBetweenTouches_;
-    bool               idle_;
-    double             normalization_;
-
-  public:
-    PanZoomMouseTracker(WorldSceneWidget& that,
-                        const std::vector<Touch>& startTouches);
-    
-    virtual bool HasRender() const
-    {
-      return false;
-    }
-
-    virtual void MouseUp()
-    {
-    }
-
-    virtual void Render(CairoContext& context,
-                        double zoom);
-
-    virtual void MouseMove(int displayX,
-                           int displayY,
-                           double x,
-                           double y,
-                           const std::vector<Touch>& displayTouches,
-                           const std::vector<Touch>& sceneTouches);
-  };
-}
--- a/Framework/Widgets/SliceViewerWidget.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,654 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "SliceViewerWidget.h"
-
-#include "../Layers/SliceOutlineRenderer.h"
-#include "../Toolbox/GeometryToolbox.h"
-#include "Framework/Layers/FrameRenderer.h"
-
-#include <Core/Logging.h>
-#include <Core/OrthancException.h>
-
-#include <boost/math/constants/constants.hpp>
-
-
-static const double THIN_SLICE_THICKNESS = 100.0 * std::numeric_limits<double>::epsilon();
-
-namespace OrthancStone
-{
-  class SliceViewerWidget::Scene : public boost::noncopyable
-  {
-  private:
-    CoordinateSystem3D            plane_;
-    double                        thickness_;
-    size_t                        countMissing_;
-    std::vector<ILayerRenderer*>  renderers_;
-
-  public:
-    void DeleteLayer(size_t index)
-    {
-      if (index >= renderers_.size())
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-      }
-
-      assert(countMissing_ <= renderers_.size());
-
-      if (renderers_[index] != NULL)
-      {
-        assert(countMissing_ < renderers_.size());
-        delete renderers_[index];
-        renderers_[index] = NULL;
-        countMissing_++;
-      }
-    }
-
-    Scene(const CoordinateSystem3D& plane,
-          double thickness,
-          size_t countLayers) :
-      plane_(plane),
-      thickness_(thickness),
-      countMissing_(countLayers),
-      renderers_(countLayers, NULL)
-    {
-      if (thickness <= 0)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-      }
-    }
-
-    ~Scene()
-    {
-      for (size_t i = 0; i < renderers_.size(); i++)
-      {
-        DeleteLayer(i);
-      }
-    }
-
-    void SetLayer(size_t index,
-                  ILayerRenderer* renderer)  // Takes ownership
-    {
-      if (renderer == NULL)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-      }
-
-      DeleteLayer(index);
-
-      renderers_[index] = renderer;
-      countMissing_--;
-    }
-
-    const CoordinateSystem3D& GetPlane() const
-    {
-      return plane_;
-    }
-
-    bool HasRenderer(size_t index)
-    {
-      return renderers_[index] != NULL;
-    }
-
-    bool IsComplete() const
-    {
-      return countMissing_ == 0;
-    }
-
-    unsigned int GetCountMissing() const
-    {
-      return static_cast<unsigned int>(countMissing_);
-    }
-
-    bool RenderScene(CairoContext& context,
-                     const ViewportGeometry& view,
-                     const CoordinateSystem3D& viewportPlane)
-    {
-      bool fullQuality = true;
-      cairo_t *cr = context.GetObject();
-
-      for (size_t i = 0; i < renderers_.size(); i++)
-      {
-        if (renderers_[i] != NULL)
-        {
-          const CoordinateSystem3D& framePlane = renderers_[i]->GetLayerPlane();
-          
-          double x0, y0, x1, y1, x2, y2;
-          viewportPlane.ProjectPoint(x0, y0, framePlane.GetOrigin());
-          viewportPlane.ProjectPoint(x1, y1, framePlane.GetOrigin() + framePlane.GetAxisX());
-          viewportPlane.ProjectPoint(x2, y2, framePlane.GetOrigin() + framePlane.GetAxisY());
-
-          /**
-           * Now we solve the system of linear equations Ax + b = x', given:
-           *   A [0 ; 0] + b = [x0 ; y0]
-           *   A [1 ; 0] + b = [x1 ; y1]
-           *   A [0 ; 1] + b = [x2 ; y2]
-           * <=>
-           *   b = [x0 ; y0]
-           *   A [1 ; 0] = [x1 ; y1] - b = [x1 - x0 ; y1 - y0]
-           *   A [0 ; 1] = [x2 ; y2] - b = [x2 - x0 ; y2 - y0]
-           * <=>
-           *   b = [x0 ; y0]
-           *   [a11 ; a21] = [x1 - x0 ; y1 - y0]
-           *   [a12 ; a22] = [x2 - x0 ; y2 - y0]
-           **/
-
-          cairo_matrix_t transform;
-          cairo_matrix_init(&transform, x1 - x0, y1 - y0, x2 - x0, y2 - y0, x0, y0);
-
-          cairo_save(cr);
-          cairo_transform(cr, &transform);
-          
-          if (!renderers_[i]->RenderLayer(context, view))
-          {
-            cairo_restore(cr);
-            return false;
-          }
-
-          cairo_restore(cr);
-        }
-
-        if (renderers_[i] != NULL &&
-            !renderers_[i]->IsFullQuality())
-        {
-          fullQuality = false;
-        }
-      }
-
-      if (!fullQuality)
-      {
-        double x, y;
-        view.MapDisplayToScene(x, y, static_cast<double>(view.GetDisplayWidth()) / 2.0, 10);
-
-        cairo_translate(cr, x, y);
-
-#if 1
-        double s = 5.0 / view.GetZoom();
-        cairo_rectangle(cr, -s, -s, 2.0 * s, 2.0 * s);
-#else
-        // TODO Drawing filled circles makes WebAssembly crash!
-        cairo_arc(cr, 0, 0, 5.0 / view.GetZoom(), 0, 2.0 * boost::math::constants::pi<double>());
-#endif
-        
-        cairo_set_line_width(cr, 2.0 / view.GetZoom());
-        cairo_set_source_rgb(cr, 1, 1, 1);
-        cairo_stroke_preserve(cr);
-        cairo_set_source_rgb(cr, 1, 0, 0);
-        cairo_fill(cr);
-      }
-
-      return true;
-    }
-
-    void SetLayerStyle(size_t index,
-                       const RenderStyle& style)
-    {
-      if (renderers_[index] != NULL)
-      {
-        renderers_[index]->SetLayerStyle(style);
-      }
-    }
-
-    bool ContainsPlane(const CoordinateSystem3D& plane) const
-    {
-      bool isOpposite;
-      if (!GeometryToolbox::IsParallelOrOpposite(isOpposite,
-                                                 plane.GetNormal(),
-                                                 plane_.GetNormal()))
-      {
-        return false;
-      }
-      else
-      {
-        double z = (plane_.ProjectAlongNormal(plane.GetOrigin()) -
-                    plane_.ProjectAlongNormal(plane_.GetOrigin()));
-
-        if (z < 0)
-        {
-          z = -z;
-        }
-
-        return z <= thickness_;
-      }
-    }
-
-    double GetThickness() const
-    {
-      return thickness_;
-    }
-  };
-
-  
-  bool SliceViewerWidget::LookupLayer(size_t& index /* out */,
-                                      const IVolumeSlicer& layer) const
-  {
-    LayersIndex::const_iterator found = layersIndex_.find(&layer);
-
-    if (found == layersIndex_.end())
-    {
-      return false;
-    }
-    else
-    {
-      index = found->second;
-      assert(index < layers_.size() &&
-             layers_[index] == &layer);
-      return true;
-    }
-  }
-
-
-  void SliceViewerWidget::GetLayerExtent(Extent2D& extent,
-                                         IVolumeSlicer& source) const
-  {
-    extent.Reset();
-
-    std::vector<Vector> points;
-    if (source.GetExtent(points, plane_))
-    {
-      for (size_t i = 0; i < points.size(); i++)
-      {
-        double x, y;
-        plane_.ProjectPoint(x, y, points[i]);
-        extent.AddPoint(x, y);
-      }
-    }
-  }
-
-
-  Extent2D SliceViewerWidget::GetSceneExtent()
-  {
-    Extent2D sceneExtent;
-
-    for (size_t i = 0; i < layers_.size(); i++)
-    {
-      assert(layers_[i] != NULL);
-      Extent2D layerExtent;
-      GetLayerExtent(layerExtent, *layers_[i]);
-
-      sceneExtent.Union(layerExtent);
-    }
-
-    return sceneExtent;
-  }
-
-  
-  bool SliceViewerWidget::RenderScene(CairoContext& context,
-                                      const ViewportGeometry& view)
-  {
-    if (currentScene_.get() != NULL)
-    {
-      return currentScene_->RenderScene(context, view, plane_);
-    }
-    else
-    {
-      return true;
-    }
-  }
-
-  
-  void SliceViewerWidget::ResetPendingScene()
-  {
-    double thickness;
-    if (pendingScene_.get() == NULL)
-    {
-      thickness = 1.0;
-    }
-    else
-    {
-      thickness = pendingScene_->GetThickness();
-    }
-    
-    pendingScene_.reset(new Scene(plane_, thickness, layers_.size()));
-  }
-  
-
-  void SliceViewerWidget::UpdateLayer(size_t index,
-                                      ILayerRenderer* renderer,
-                                      const CoordinateSystem3D& plane)
-  {
-    LOG(INFO) << "Updating layer " << index;
-    
-    std::auto_ptr<ILayerRenderer> tmp(renderer);
-
-    if (renderer == NULL)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-    }
-
-    if (index >= layers_.size())
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-
-    assert(layers_.size() == styles_.size());
-    renderer->SetLayerStyle(styles_[index]);
-
-    if (currentScene_.get() != NULL &&
-        currentScene_->ContainsPlane(plane))
-    {
-      currentScene_->SetLayer(index, tmp.release());
-      NotifyContentChanged();
-    }
-    else if (pendingScene_.get() != NULL &&
-             pendingScene_->ContainsPlane(plane))
-    {
-      pendingScene_->SetLayer(index, tmp.release());
-
-      if (currentScene_.get() == NULL ||
-          !currentScene_->IsComplete() ||
-          pendingScene_->IsComplete())
-      {
-        currentScene_ = pendingScene_;
-        NotifyContentChanged();
-      }
-    }
-  }
-
-  
-  SliceViewerWidget::SliceViewerWidget(MessageBroker& broker, 
-                                       const std::string& name) :
-    WorldSceneWidget(name),
-    IObserver(broker),
-    IObservable(broker),
-    started_(false)
-  {
-    SetBackgroundCleared(true);
-  }
-  
-  
-  SliceViewerWidget::~SliceViewerWidget()
-  {
-    for (size_t i = 0; i < layers_.size(); i++)
-    {
-      delete layers_[i];
-    }
-  }
-  
-  void SliceViewerWidget::ObserveLayer(IVolumeSlicer& layer)
-  {
-    layer.RegisterObserverCallback(new Callable<SliceViewerWidget, IVolumeSlicer::GeometryReadyMessage>
-                                   (*this, &SliceViewerWidget::OnGeometryReady));
-    // currently ignore errors layer->RegisterObserverCallback(new Callable<SliceViewerWidget, IVolumeSlicer::GeometryErrorMessage>(*this, &SliceViewerWidget::...));
-    layer.RegisterObserverCallback(new Callable<SliceViewerWidget, IVolumeSlicer::SliceContentChangedMessage>
-                                   (*this, &SliceViewerWidget::OnSliceChanged));
-    layer.RegisterObserverCallback(new Callable<SliceViewerWidget, IVolumeSlicer::ContentChangedMessage>
-                                   (*this, &SliceViewerWidget::OnContentChanged));
-    layer.RegisterObserverCallback(new Callable<SliceViewerWidget, IVolumeSlicer::LayerReadyMessage>
-                                   (*this, &SliceViewerWidget::OnLayerReady));
-    layer.RegisterObserverCallback(new Callable<SliceViewerWidget, IVolumeSlicer::LayerErrorMessage>
-                                   (*this, &SliceViewerWidget::OnLayerError));
-  }
-
-
-  size_t SliceViewerWidget::AddLayer(IVolumeSlicer* layer)  // Takes ownership
-  {
-    if (layer == NULL)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-    }
-
-    size_t index = layers_.size();
-    layers_.push_back(layer);
-    styles_.push_back(RenderStyle());
-    layersIndex_[layer] = index;
-
-    ResetPendingScene();
-
-    ObserveLayer(*layer);
-
-    ResetChangedLayers();
-
-    return index;
-  }
-
-
-  void SliceViewerWidget::ReplaceLayer(size_t index, IVolumeSlicer* layer)  // Takes ownership
-  {
-    if (layer == NULL)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-    }
-
-    if (index >= layers_.size())
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-
-    delete layers_[index];
-    layers_[index] = layer;
-    layersIndex_[layer] = index;
-
-    ResetPendingScene();
-
-    ObserveLayer(*layer);
-
-    InvalidateLayer(index);
-  }
-
-
-  void SliceViewerWidget::RemoveLayer(size_t index)
-  {
-    if (index >= layers_.size())
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-
-    IVolumeSlicer* previousLayer = layers_[index];
-    layersIndex_.erase(layersIndex_.find(previousLayer));
-    layers_.erase(layers_.begin() + index);
-    changedLayers_.erase(changedLayers_.begin() + index);
-    styles_.erase(styles_.begin() + index);
-
-    delete layers_[index];
-
-    currentScene_->DeleteLayer(index);
-    ResetPendingScene();
-
-    NotifyContentChanged();
-  }
-
-
-  const RenderStyle& SliceViewerWidget::GetLayerStyle(size_t layer) const
-  {
-    if (layer >= layers_.size())
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-
-    assert(layers_.size() == styles_.size());
-    return styles_[layer];
-  }
-  
-
-  void SliceViewerWidget::SetLayerStyle(size_t layer,
-                                        const RenderStyle& style)
-  {
-    if (layer >= layers_.size())
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-
-    assert(layers_.size() == styles_.size());
-    styles_[layer] = style;
-
-    if (currentScene_.get() != NULL)
-    {
-      currentScene_->SetLayerStyle(layer, style);
-    }
-
-    if (pendingScene_.get() != NULL)
-    {
-      pendingScene_->SetLayerStyle(layer, style);
-    }
-
-    NotifyContentChanged();
-  }
-  
-
-  void SliceViewerWidget::SetSlice(const CoordinateSystem3D& plane)
-  {
-    LOG(INFO) << "Setting slice origin: (" << plane.GetOrigin()[0]
-              << "," << plane.GetOrigin()[1]
-              << "," << plane.GetOrigin()[2] << ")";
-    
-    Deprecated::Slice displayedSlice(plane_, THIN_SLICE_THICKNESS);
-
-    //if (!displayedSlice.ContainsPlane(slice))
-    {
-      if (currentScene_.get() == NULL ||
-          (pendingScene_.get() != NULL &&
-           pendingScene_->IsComplete()))
-      {
-        currentScene_ = pendingScene_;
-      }
-
-      plane_ = plane;
-      ResetPendingScene();
-
-      InvalidateAllLayers();   // TODO Removing this line avoid loading twice the image in WASM
-    }
-
-    BroadcastMessage(DisplayedSliceMessage(*this, displayedSlice));
-  }
-
-
-  void SliceViewerWidget::OnGeometryReady(const IVolumeSlicer::GeometryReadyMessage& message)
-  {
-    size_t i;
-    if (LookupLayer(i, message.GetOrigin()))
-    {
-      LOG(INFO) << ": Geometry ready for layer " << i << " in " << GetName();
-
-      changedLayers_[i] = true;
-      //layers_[i]->ScheduleLayerCreation(plane_);
-    }
-    BroadcastMessage(GeometryChangedMessage(*this));
-  }
-  
-
-  void SliceViewerWidget::InvalidateAllLayers()
-  {
-    for (size_t i = 0; i < layers_.size(); i++)
-    {
-      assert(layers_[i] != NULL);
-      changedLayers_[i] = true;
-      
-      //layers_[i]->ScheduleLayerCreation(plane_);
-    }
-  }
-
-
-  void SliceViewerWidget::InvalidateLayer(size_t layer)
-  {
-    if (layer >= layers_.size())
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-
-    assert(layers_[layer] != NULL);
-    changedLayers_[layer] = true;
-
-    //layers_[layer]->ScheduleLayerCreation(plane_);
-  }
-
-
-  void SliceViewerWidget::OnContentChanged(const IVolumeSlicer::ContentChangedMessage& message)
-  {
-    size_t index;
-    if (LookupLayer(index, message.GetOrigin()))
-    {
-      InvalidateLayer(index);
-    }
-    
-    BroadcastMessage(SliceViewerWidget::ContentChangedMessage(*this));
-  }
-  
-
-  void SliceViewerWidget::OnSliceChanged(const IVolumeSlicer::SliceContentChangedMessage& message)
-  {
-    if (message.GetSlice().ContainsPlane(plane_))
-    {
-      size_t index;
-      if (LookupLayer(index, message.GetOrigin()))
-      {
-        InvalidateLayer(index);
-      }
-    }
-    
-    BroadcastMessage(SliceViewerWidget::ContentChangedMessage(*this));
-  }
-  
-  
-  void SliceViewerWidget::OnLayerReady(const IVolumeSlicer::LayerReadyMessage& message)
-  {
-    size_t index;
-    if (LookupLayer(index, message.GetOrigin()))
-    {
-      LOG(INFO) << "Renderer ready for layer " << index;
-      UpdateLayer(index, message.CreateRenderer(), message.GetSlice());
-    }
-    
-    BroadcastMessage(SliceViewerWidget::ContentChangedMessage(*this));
-  }
-
-
-  void SliceViewerWidget::OnLayerError(const IVolumeSlicer::LayerErrorMessage& message)
-  {
-    size_t index;
-    if (LookupLayer(index, message.GetOrigin()))
-    {
-      LOG(ERROR) << "Using error renderer on layer " << index;
-
-      // TODO
-      //UpdateLayer(index, new SliceOutlineRenderer(slice), slice);
-
-      BroadcastMessage(SliceViewerWidget::ContentChangedMessage(*this));
-    }
-  }
-
-
-  void SliceViewerWidget::ResetChangedLayers()
-  {
-    changedLayers_.resize(layers_.size());
-
-    for (size_t i = 0; i < changedLayers_.size(); i++)
-    {
-      changedLayers_[i] = false;
-    }
-  }
-
-
-  void SliceViewerWidget::DoAnimation()
-  {
-    assert(changedLayers_.size() <= layers_.size());
-    
-    for (size_t i = 0; i < changedLayers_.size(); i++)
-    {
-      if (changedLayers_[i])
-      {
-        layers_[i]->ScheduleLayerCreation(plane_);
-      }
-    }
-    
-    ResetChangedLayers();
-  }
-}
--- a/Framework/Widgets/SliceViewerWidget.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,155 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "WorldSceneWidget.h"
-#include "../Layers/IVolumeSlicer.h"
-#include "../Toolbox/Extent2D.h"
-#include "../../Framework/Messages/IObserver.h"
-
-#include <map>
-
-namespace OrthancStone
-{
-  class SliceViewerWidget :
-    public WorldSceneWidget,
-    public IObserver,
-    public IObservable
-  {
-  public:
-    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, GeometryChangedMessage, SliceViewerWidget);
-    ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, ContentChangedMessage, SliceViewerWidget);
-
-
-    // TODO - Use this message in ReferenceLineSource
-    class DisplayedSliceMessage : public OriginMessage<SliceViewerWidget>
-    {
-      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
-      
-    private:
-      const Deprecated::Slice& slice_;
-
-    public:
-      DisplayedSliceMessage(SliceViewerWidget& origin,
-                            const Deprecated::Slice& slice) :
-        OriginMessage(origin),
-        slice_(slice)
-      {
-      }
-
-      const Deprecated::Slice& GetSlice() const
-      {
-        return slice_;
-      }
-    };
-
-  private:
-    SliceViewerWidget(const SliceViewerWidget&);
-    SliceViewerWidget& operator=(const SliceViewerWidget&);
-
-    class Scene;
-    
-    typedef std::map<const IVolumeSlicer*, size_t>  LayersIndex;
-
-    bool                         started_;
-    LayersIndex                  layersIndex_;
-    std::vector<IVolumeSlicer*>  layers_;
-    std::vector<RenderStyle>     styles_;
-    CoordinateSystem3D           plane_;
-    std::auto_ptr<Scene>         currentScene_;
-    std::auto_ptr<Scene>         pendingScene_;
-    std::vector<bool>            changedLayers_;
-
-    bool LookupLayer(size_t& index /* out */,
-                     const IVolumeSlicer& layer) const;
-
-    void GetLayerExtent(Extent2D& extent,
-                        IVolumeSlicer& source) const;
-
-    void OnGeometryReady(const IVolumeSlicer::GeometryReadyMessage& message);
-
-    virtual void OnContentChanged(const IVolumeSlicer::ContentChangedMessage& message);
-
-    virtual void OnSliceChanged(const IVolumeSlicer::SliceContentChangedMessage& message);
-
-    virtual void OnLayerReady(const IVolumeSlicer::LayerReadyMessage& message);
-
-    virtual void OnLayerError(const IVolumeSlicer::LayerErrorMessage& message);
-
-    void ObserveLayer(IVolumeSlicer& source);
-
-    void ResetChangedLayers();
-
-  public:
-    SliceViewerWidget(MessageBroker& broker, 
-                      const std::string& name);
-
-    virtual Extent2D GetSceneExtent();
-
-  protected:
-    virtual bool RenderScene(CairoContext& context,
-                             const ViewportGeometry& view);
-
-    void ResetPendingScene();
-
-    void UpdateLayer(size_t index,
-                     ILayerRenderer* renderer,
-                     const CoordinateSystem3D& plane);
-
-    void InvalidateAllLayers();
-
-    void InvalidateLayer(size_t layer);
-    
-  public:
-    virtual ~SliceViewerWidget();
-
-    size_t AddLayer(IVolumeSlicer* layer);  // Takes ownership
-
-    void ReplaceLayer(size_t layerIndex, IVolumeSlicer* layer); // Takes ownership
-
-    void RemoveLayer(size_t layerIndex);
-
-    size_t GetLayerCount() const
-    {
-      return layers_.size();
-    }
-
-    const RenderStyle& GetLayerStyle(size_t layer) const;
-
-    void SetLayerStyle(size_t layer,
-                       const RenderStyle& style);
-
-    void SetSlice(const CoordinateSystem3D& plane);
-
-    const CoordinateSystem3D& GetSlice() const
-    {
-      return plane_;
-    }
-
-    virtual bool HasAnimation() const
-    {
-      return true;
-    }
-
-    virtual void DoAnimation();
-  };
-}
--- a/Framework/Widgets/TestCairoWidget.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,126 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "TestCairoWidget.h"
-
-#include <stdio.h>
-
-
-namespace OrthancStone
-{
-  namespace Samples
-  {
-    void TestCairoWidget::DoAnimation() 
-    {
-      value_ -= 0.01f;
-      if (value_ < 0)
-      {
-        value_ = 1;
-      }
-
-      NotifyContentChanged();
-    }
-
-
-    bool TestCairoWidget::RenderCairo(CairoContext& context)
-    {
-      cairo_t* cr = context.GetObject();
-
-      cairo_set_source_rgb (cr, .3, 0, 0);
-      cairo_paint(cr);
-
-      cairo_set_source_rgb(cr, 0, 1, 0);
-      cairo_rectangle(cr, width_ / 4, height_ / 4, width_ / 2, height_ / 2);
-      cairo_set_line_width(cr, 1.0);
-      cairo_fill(cr);
-
-      cairo_set_source_rgb(cr, 0, 1, value_);
-      cairo_rectangle(cr, width_ / 2 - 50, height_ / 2 - 50, 100, 100);
-      cairo_fill(cr);
-
-      return true;
-    }
-
-
-    void TestCairoWidget::RenderMouseOverCairo(CairoContext& context,
-                                               int x,
-                                               int y)
-    {
-      cairo_t* cr = context.GetObject();
-
-      cairo_set_source_rgb (cr, 1, 0, 0);
-      cairo_rectangle(cr, x - 5, y - 5, 10, 10);
-      cairo_set_line_width(cr, 1.0);
-      cairo_stroke(cr);
-
-      char buf[64];
-      sprintf(buf, "(%d,%d)", x, y);
-      UpdateStatusBar(buf);
-    }
-
-
-    TestCairoWidget::TestCairoWidget(const std::string& name, bool animate) :
-      CairoWidget(name),
-      width_(0),
-      height_(0),
-      value_(1),
-      animate_(animate)
-    {
-    }
-
-
-    void TestCairoWidget::SetSize(unsigned int width, 
-                                  unsigned int height)
-    {
-      CairoWidget::SetSize(width, height);
-      width_ = width;
-      height_ = height;
-    }
- 
-
-    IMouseTracker* TestCairoWidget::CreateMouseTracker(MouseButton button,
-                                                       int x,
-                                                       int y,
-                                                       KeyboardModifiers modifiers,
-                                                       const std::vector<Touch>& touches)
-    {
-      UpdateStatusBar("Click");
-      return NULL;
-    }
-
-
-    void TestCairoWidget::MouseWheel(MouseWheelDirection direction,
-                                     int x,
-                                     int y,
-                                     KeyboardModifiers modifiers) 
-    {
-      UpdateStatusBar(direction == MouseWheelDirection_Down ? "Wheel down" : "Wheel up");
-    }
-
-    
-    void TestCairoWidget::KeyPressed(KeyboardKeys key,
-                                     char keyChar,
-                                     KeyboardModifiers modifiers)
-    {
-      UpdateStatusBar("Key pressed: \"" + std::string(1, keyChar) + "\"");
-    }
-  }
-}
--- a/Framework/Widgets/TestCairoWidget.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "CairoWidget.h"
-
-namespace OrthancStone
-{
-  namespace Samples
-  {
-    class TestCairoWidget : public CairoWidget
-    {
-    private:
-      unsigned int  width_;
-      unsigned int  height_;
-      float         value_;
-      bool          animate_;
-
-    protected:
-      virtual bool RenderCairo(CairoContext& context);
-
-      virtual void RenderMouseOverCairo(CairoContext& context,
-                                        int x,
-                                        int y);
-
-    public:
-      TestCairoWidget(const std::string& name, bool animate);
-
-      virtual void SetSize(unsigned int width, 
-                           unsigned int height);
- 
-      virtual IMouseTracker* CreateMouseTracker(MouseButton button,
-                                                int x,
-                                                int y,
-                                                KeyboardModifiers modifiers,
-                                                const std::vector<Touch>& touches);
-
-      virtual void MouseWheel(MouseWheelDirection direction,
-                              int x,
-                              int y,
-                              KeyboardModifiers modifiers);
-    
-      virtual void KeyPressed(KeyboardKeys key,
-                              char keyChar,
-                              KeyboardModifiers modifiers);
-
-      virtual bool HasAnimation() const
-      {
-        return animate_;
-      }
-      
-      virtual void DoAnimation();
-
-      virtual bool HasRenderMouseOver()
-      {
-        return true;
-      }
-    };
-  }
-}
--- a/Framework/Widgets/TestWorldSceneWidget.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,149 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "TestWorldSceneWidget.h"
-
-#include <Core/OrthancException.h>
-
-#include <math.h>
-#include <stdio.h>
-
-namespace OrthancStone
-{
-  namespace Samples
-  {
-    class TestWorldSceneWidget::Interactor : public IWorldSceneInteractor
-    {
-    public:
-      virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget,
-                                                          const ViewportGeometry& view,
-                                                          MouseButton button,
-                                                          KeyboardModifiers modifiers,
-                                                          int viewportX,
-                                                          int viewportY,
-                                                          double x,
-                                                          double y,
-                                                          IStatusBar* statusBar,
-                                                          const std::vector<Touch>& touches)
-      {
-        if (statusBar)
-        {
-          char buf[64];
-          sprintf(buf, "X = %0.2f, Y = %0.2f", x, y);
-          statusBar->SetMessage(buf);
-        }
-
-        return NULL;
-      }
-
-      virtual void MouseOver(CairoContext& context,
-                             WorldSceneWidget& widget,
-                             const ViewportGeometry& view,
-                             double x,
-                             double y,
-                             IStatusBar* statusBar)
-      {
-        double S = 0.5;
-
-        if (fabs(x) <= S &&
-            fabs(y) <= S)
-        {
-          cairo_t* cr = context.GetObject();
-          cairo_set_source_rgb(cr, 1, 0, 0);
-          cairo_rectangle(cr, -S, -S , 2.0 * S, 2.0 * S);
-          cairo_set_line_width(cr, 1.0 / view.GetZoom());
-          cairo_stroke(cr);
-        }
-      }
-
-      virtual void MouseWheel(WorldSceneWidget& widget,
-                              MouseWheelDirection direction,
-                              KeyboardModifiers modifiers,
-                              IStatusBar* statusBar)
-      {
-        if (statusBar)
-        {
-          statusBar->SetMessage(direction == MouseWheelDirection_Down ? "Wheel down" : "Wheel up");
-        }
-      }
-
-      virtual void KeyPressed(WorldSceneWidget& widget,
-                              KeyboardKeys key,
-                              char keyChar,
-                              KeyboardModifiers modifiers,
-                              IStatusBar* statusBar)
-      {
-        if (statusBar)
-        {
-          statusBar->SetMessage("Key pressed: \"" + std::string(1, keyChar) + "\"");
-        }
-      }
-    };
-
-
-    bool TestWorldSceneWidget::RenderScene(CairoContext& context,
-                                           const ViewportGeometry& view)
-    {
-      cairo_t* cr = context.GetObject();
-
-      // Clear background
-      cairo_set_source_rgb(cr, 0, 0, 0);
-      cairo_paint(cr);
-
-      float color = static_cast<float>(count_ % 16) / 15.0f;
-      cairo_set_source_rgb(cr, 0, 1.0f - color, color);
-      cairo_rectangle(cr, -10, -.5, 20, 1);
-      cairo_fill(cr);
-
-      return true;
-    }
-
-
-    TestWorldSceneWidget::TestWorldSceneWidget(const std::string& name, bool animate) :
-      WorldSceneWidget(name),
-      interactor_(new Interactor),
-      animate_(animate),
-      count_(0)
-    {
-      SetInteractor(*interactor_);
-    }
-
-
-    Extent2D TestWorldSceneWidget::GetSceneExtent()
-    {
-      return Extent2D(-10, -.5, 10, .5);
-    }
-
-
-    void TestWorldSceneWidget::DoAnimation()
-    {
-      if (animate_)
-      {
-        count_++;
-        NotifyContentChanged();
-      }
-      else
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-      }
-    }
-  }
-}
--- a/Framework/Widgets/TestWorldSceneWidget.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "WorldSceneWidget.h"
-
-#include <memory>
-
-namespace OrthancStone
-{
-  namespace Samples
-  {
-    class TestWorldSceneWidget : public WorldSceneWidget
-    {
-    private:
-      class Interactor;
-
-      std::auto_ptr<Interactor>   interactor_;
-      bool                        animate_;
-      unsigned int                count_;
-
-    protected:
-      virtual bool RenderScene(CairoContext& context,
-                               const ViewportGeometry& view);
-
-    public:
-      TestWorldSceneWidget(const std::string& name, bool animate);
-
-      virtual Extent2D GetSceneExtent();
-
-      virtual bool HasAnimation() const
-      {
-        return animate_;
-      }
-
-      virtual void DoAnimation();
-
-      virtual bool HasRenderMouseOver()
-      {
-        return true;
-      }
-    };
-  }
-}
--- a/Framework/Widgets/WidgetBase.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,166 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "WidgetBase.h"
-
-#include <Core/OrthancException.h>
-#include <Core/Images/ImageProcessing.h>
-#include <Core/Logging.h>
-
-namespace OrthancStone
-{
-  void WidgetBase::NotifyContentChanged()
-  {
-    if (parent_ != NULL)
-    {
-      parent_->NotifyContentChanged();
-    }
-
-    if (viewport_ != NULL)
-    {
-      viewport_->NotifyBackgroundChanged();
-    }
-  }
-
-
-  void WidgetBase::SetParent(IWidget& parent)
-  {
-    if (parent_ != NULL)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-    else
-    {
-      parent_ = &parent;
-    }
-  }    
-
-  
-  void WidgetBase::ClearBackgroundOrthanc(Orthanc::ImageAccessor& target) const 
-  {
-    // Clear the background using Orthanc
-
-    if (backgroundCleared_)
-    {
-      Orthanc::ImageProcessing::Set(target, 
-                                    backgroundColor_[0],
-                                    backgroundColor_[1],
-                                    backgroundColor_[2],
-                                    255 /* alpha */);
-    }
-  }
-
-
-  void WidgetBase::ClearBackgroundCairo(CairoContext& context) const
-  {
-    // Clear the background using Cairo
-
-    if (IsBackgroundCleared())
-    {
-      uint8_t red, green, blue;
-      GetBackgroundColor(red, green, blue);
-
-      context.SetSourceColor(red, green, blue);
-      cairo_paint(context.GetObject());
-    }
-  }
-
-
-  void WidgetBase::ClearBackgroundCairo(Orthanc::ImageAccessor& target) const
-  {
-    CairoSurface surface(target, false /* no alpha */);
-    CairoContext context(surface);
-    ClearBackgroundCairo(context);
-  }
-
-
-  void WidgetBase::UpdateStatusBar(const std::string& message)
-  {
-    if (statusBar_ != NULL)
-    {
-      statusBar_->SetMessage(message);
-    }
-  }
-
-
-  WidgetBase::WidgetBase(const std::string& name) :
-    parent_(NULL),
-    viewport_(NULL),
-    statusBar_(NULL),
-    backgroundCleared_(false),
-    transmitMouseOver_(false),
-    name_(name)
-  {
-    backgroundColor_[0] = 0;
-    backgroundColor_[1] = 0;
-    backgroundColor_[2] = 0;
-  }
-
-
-  void WidgetBase::SetViewport(WidgetViewport& viewport)
-  {
-    if (viewport_ != NULL)
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-    }
-    else
-    {
-      viewport_ = &viewport;
-    }
-  }
-
-  
-  void WidgetBase::SetBackgroundColor(uint8_t red,
-                                      uint8_t green,
-                                      uint8_t blue)
-  {
-    backgroundColor_[0] = red;
-    backgroundColor_[1] = green;
-    backgroundColor_[2] = blue;
-  }
-
-  void WidgetBase::GetBackgroundColor(uint8_t& red,
-                                      uint8_t& green,
-                                      uint8_t& blue) const
-  {
-    red = backgroundColor_[0];
-    green = backgroundColor_[1];
-    blue = backgroundColor_[2];
-  }
-
-
-  bool WidgetBase::Render(Orthanc::ImageAccessor& surface)
-  {
-#if 0
-    ClearBackgroundOrthanc(surface);
-#else
-    ClearBackgroundCairo(surface);  // Faster than Orthanc
-#endif
-
-    return true;
-  }
-
-  
-  void WidgetBase::DoAnimation()
-  {
-    throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-  }
-}
--- a/Framework/Widgets/WidgetBase.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,117 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- * 
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "IWidget.h"
-
-#include "../Viewport/CairoContext.h"
-#include "../Viewport/WidgetViewport.h"
-
-namespace OrthancStone
-{
-  class WidgetBase : public IWidget
-  {
-  private:
-    IWidget*         parent_;
-    WidgetViewport*  viewport_;
-    IStatusBar*      statusBar_;
-    bool             backgroundCleared_;
-    uint8_t          backgroundColor_[3];
-    bool             transmitMouseOver_;
-    std::string      name_;
-
-  protected:
-    void ClearBackgroundOrthanc(Orthanc::ImageAccessor& target) const;
-
-    void ClearBackgroundCairo(CairoContext& context) const;
-
-    void ClearBackgroundCairo(Orthanc::ImageAccessor& target) const;
-
-    void UpdateStatusBar(const std::string& message);
-
-    IStatusBar* GetStatusBar() const
-    {
-      return statusBar_;
-    }
-
-  public:
-    WidgetBase(const std::string& name);
-
-    virtual void FitContent()
-    {
-    }
-  
-    virtual void SetParent(IWidget& parent);
-    
-    virtual void SetViewport(WidgetViewport& viewport);
-
-    void SetBackgroundCleared(bool clear)
-    {
-      backgroundCleared_ = clear;
-    }
-
-    bool IsBackgroundCleared() const
-    {
-      return backgroundCleared_;
-    }
-
-    void SetTransmitMouseOver(bool transmit)
-    {
-      transmitMouseOver_ = transmit;
-    }
-
-    void SetBackgroundColor(uint8_t red,
-                            uint8_t green,
-                            uint8_t blue);
-
-    void GetBackgroundColor(uint8_t& red,
-                            uint8_t& green,
-                            uint8_t& blue) const;
-
-    virtual void SetStatusBar(IStatusBar& statusBar)
-    {
-      statusBar_ = &statusBar;
-    }
-
-    virtual bool Render(Orthanc::ImageAccessor& surface);
-
-    virtual bool HasAnimation() const
-    {
-      return false;
-    }
-
-    virtual void DoAnimation();
-
-    virtual bool HasRenderMouseOver()
-    {
-      return transmitMouseOver_;
-    }
-
-    virtual void NotifyContentChanged();
-
-    const std::string& GetName() const
-    {
-      return name_;
-    }
-
-  };
-}
--- a/Framework/Widgets/WorldSceneWidget.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,231 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "WorldSceneWidget.h"
-
-#include "PanMouseTracker.h"
-#include "ZoomMouseTracker.h"
-#include "PanZoomMouseTracker.h"
-
-#include <Core/Logging.h>
-#include <Core/OrthancException.h>
-
-#include <math.h>
-#include <memory>
-#include <cassert>
-
-namespace OrthancStone
-{
-  // this is an adapter between a IWorldSceneMouseTracker
-  // that is tracking a mouse in scene coordinates/mm and
-  // an IMouseTracker that is tracking a mouse
-  // in screen coordinates/pixels.
-  class WorldSceneWidget::SceneMouseTracker : public IMouseTracker
-  {
-  private:
-    ViewportGeometry                        view_;
-    std::auto_ptr<IWorldSceneMouseTracker>  tracker_;
-
-  public:
-    SceneMouseTracker(const ViewportGeometry& view,
-                      IWorldSceneMouseTracker* tracker) :
-      view_(view),
-      tracker_(tracker)
-    {
-      if (tracker == NULL)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-      }
-    }
-
-    virtual void Render(Orthanc::ImageAccessor& target)
-    {
-      if (tracker_->HasRender())
-      {
-        CairoSurface surface(target, false /* no alpha */);
-        CairoContext context(surface);
-        view_.ApplyTransform(context);
-        tracker_->Render(context, view_.GetZoom());
-      }
-    }
-
-    virtual void MouseUp()
-    {
-      tracker_->MouseUp();
-    }
-
-    virtual void MouseMove(int x,
-                           int y,
-                           const std::vector<Touch>& displayTouches)
-    {
-      double sceneX, sceneY;
-      view_.MapPixelCenterToScene(sceneX, sceneY, x, y);
-
-      std::vector<Touch> sceneTouches;
-      for (size_t t = 0; t < displayTouches.size(); t++)
-      {
-        double sx, sy;
-        
-        view_.MapPixelCenterToScene(
-          sx, sy, (int)displayTouches[t].x, (int)displayTouches[t].y);
-        
-        sceneTouches.push_back(
-          Touch(static_cast<float>(sx), static_cast<float>(sy)));
-      }
-      tracker_->MouseMove(x, y, sceneX, sceneY, displayTouches, sceneTouches);
-    }
-  };
-
-
-  bool WorldSceneWidget::RenderCairo(CairoContext& context)
-  {
-    view_.ApplyTransform(context);
-    return RenderScene(context, view_);
-  }
-
-
-  void WorldSceneWidget::RenderMouseOverCairo(CairoContext& context,
-                                              int x,
-                                              int y)
-  {
-    ViewportGeometry view = GetView();
-    view.ApplyTransform(context);
-
-    double sceneX, sceneY;
-    view.MapPixelCenterToScene(sceneX, sceneY, x, y);
-
-    if (interactor_)
-    {
-      interactor_->MouseOver(context, *this, view, sceneX, sceneY, GetStatusBar());
-    }
-  }
-
-
-  void WorldSceneWidget::SetSceneExtent(ViewportGeometry& view)
-  {
-    view.SetSceneExtent(GetSceneExtent());
-  }
-
-
-  void WorldSceneWidget::SetSize(unsigned int width,
-                                 unsigned int height)
-  {
-    CairoWidget::SetSize(width, height);
-    view_.SetDisplaySize(width, height);
-  }
-
-
-  void WorldSceneWidget::SetInteractor(IWorldSceneInteractor& interactor)
-  {
-    interactor_ = &interactor;
-  }
-
-
-  void WorldSceneWidget::FitContent()
-  {
-    SetSceneExtent(view_);
-    view_.FitContent();
-
-    NotifyContentChanged();
-  }
-
-
-  void WorldSceneWidget::SetView(const ViewportGeometry& view)
-  {
-    view_ = view;
-
-    NotifyContentChanged();
-  }
-
-
-  IMouseTracker* WorldSceneWidget::CreateMouseTracker(MouseButton button,
-                                                      int x,
-                                                      int y,
-                                                      KeyboardModifiers modifiers,
-                                                      const std::vector<Touch>& touches)
-  {
-    double sceneX, sceneY;
-    view_.MapPixelCenterToScene(sceneX, sceneY, x, y);
-
-    // asks the Widget Interactor to provide a mouse tracker
-    std::auto_ptr<IWorldSceneMouseTracker> tracker;
-
-    if (interactor_)
-    {
-      tracker.reset(interactor_->CreateMouseTracker(*this, view_, button, modifiers, x, y, sceneX, sceneY, GetStatusBar(), touches));
-    }
-    
-    if (tracker.get() != NULL)
-    {
-      return new SceneMouseTracker(view_, tracker.release());
-    }
-    else if (hasDefaultMouseEvents_)
-    {
-      printf("has default mouse events\n");
-      if (touches.size() == 2)
-      {
-        printf("2 touches !\n");
-        return new SceneMouseTracker(view_, new PanZoomMouseTracker(*this, touches));
-      }
-      else
-      {
-        switch (button)
-        {
-          case MouseButton_Middle:
-            return new SceneMouseTracker(view_, new PanMouseTracker(*this, x, y));
-
-          case MouseButton_Right:
-            return new SceneMouseTracker(view_, new ZoomMouseTracker(*this, x, y));
-
-          default:
-            return NULL;
-        }
-      }
-    }
-    else
-    {
-      return NULL;
-    }
-  }
-
-
-  void WorldSceneWidget::MouseWheel(MouseWheelDirection direction,
-                                    int x,
-                                    int y,
-                                    KeyboardModifiers modifiers)
-  {
-    if (interactor_)
-    {
-      interactor_->MouseWheel(*this, direction, modifiers, GetStatusBar());
-    }
-  }
-
-
-  void WorldSceneWidget::KeyPressed(KeyboardKeys key,
-                                    char keyChar,
-                                    KeyboardModifiers modifiers)
-  {
-    if (interactor_)
-    {
-      interactor_->KeyPressed(*this, key, keyChar, modifiers, GetStatusBar());
-    }
-  }
-}
--- a/Framework/Widgets/WorldSceneWidget.h	Wed May 22 16:01:34 2019 +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-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "CairoWidget.h"
-#include "IWorldSceneInteractor.h"
-
-#include "../Toolbox/ViewportGeometry.h"
-
-namespace OrthancStone
-{
-  class WorldSceneWidget : public CairoWidget
-  {
-  private:
-    class SceneMouseTracker;
-
-    ViewportGeometry       view_;
-    IWorldSceneInteractor* interactor_;
-    bool                   hasDefaultMouseEvents_;
-
-  protected:
-    virtual Extent2D GetSceneExtent() = 0;
-
-    virtual bool RenderScene(CairoContext& context,
-                             const ViewportGeometry& view) = 0;
-
-    // From CairoWidget
-    virtual bool RenderCairo(CairoContext& context);
-
-    // From CairoWidget
-    virtual void RenderMouseOverCairo(CairoContext& context,
-                                      int x,
-                                      int y);
-
-    void SetSceneExtent(ViewportGeometry& geometry);
-
-  public:
-    WorldSceneWidget(const std::string& name) :
-      CairoWidget(name),
-      interactor_(NULL),
-      hasDefaultMouseEvents_(true)
-    {
-    }
-
-    void SetDefaultMouseEvents(bool value)
-    {
-      hasDefaultMouseEvents_ = value;
-    }
-
-    bool HasDefaultMouseEvents() const
-    {
-      return hasDefaultMouseEvents_;
-    }
-
-    void SetInteractor(IWorldSceneInteractor& interactor);
-
-    void SetView(const ViewportGeometry& view);
-
-    const ViewportGeometry& GetView() const
-    {
-      return view_;
-    }
-
-    virtual void SetSize(unsigned int width,
-                         unsigned int height);
-
-    virtual void FitContent();
-
-    virtual IMouseTracker* CreateMouseTracker(MouseButton button,
-                                              int x,
-                                              int y,
-                                              KeyboardModifiers modifiers,
-                                              const std::vector<Touch>& touches);
-
-    virtual void MouseWheel(MouseWheelDirection direction,
-                            int x,
-                            int y,
-                            KeyboardModifiers modifiers);
-
-    virtual void KeyPressed(KeyboardKeys key,
-                            char keyChar,
-                            KeyboardModifiers modifiers);
-  };
-}
--- a/Framework/Widgets/ZoomMouseTracker.cpp	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,110 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "ZoomMouseTracker.h"
-
-#include <Core/Logging.h>
-#include <Core/OrthancException.h>
-
-namespace OrthancStone
-{
-  ZoomMouseTracker::ZoomMouseTracker(WorldSceneWidget& that,
-                                     int x,
-                                     int y) :
-    that_(that),
-    originalZoom_(that.GetView().GetZoom()),
-    downX_(x),
-    downY_(y)
-  {
-    that.GetView().MapPixelCenterToScene(centerX_, centerY_, x, y);
-
-    unsigned int height = that.GetView().GetDisplayHeight();
-      
-    if (height <= 3)
-    {
-      idle_ = true;
-      LOG(WARNING) << "image is too small to zoom (current height = " << height << ")";
-    }
-    else
-    {
-      idle_ = false;
-      normalization_ = 1.0 / static_cast<double>(height - 1);
-    }
-  }
-    
-
-  void ZoomMouseTracker::Render(CairoContext& context,
-                                double zoom)
-  {
-    throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-  }
-
-
-  void ZoomMouseTracker::MouseMove(int displayX,
-                                   int displayY,
-                                   double x,
-                                   double y,
-                                   const std::vector<Touch>& displayTouches,
-                                   const std::vector<Touch>& sceneTouches)
-  {
-    static const double MIN_ZOOM = -4;
-    static const double MAX_ZOOM = 4;
-
-      
-    if (!idle_)
-    {
-      double dy = static_cast<double>(displayY - downY_) * normalization_;  // In the range [-1,1]
-      double z;
-
-      // Linear interpolation from [-1, 1] to [MIN_ZOOM, MAX_ZOOM]
-      if (dy < -1.0)
-      {
-        z = MIN_ZOOM;
-      }
-      else if (dy > 1.0)
-      {
-        z = MAX_ZOOM;
-      }
-      else
-      {
-        z = MIN_ZOOM + (MAX_ZOOM - MIN_ZOOM) * (dy + 1.0) / 2.0;
-      }
-
-      z = pow(2.0, z);
-
-      ViewportGeometry view = that_.GetView();
-        
-      view.SetZoom(z * originalZoom_);
-        
-      // Correct the pan so that the original click point is kept at
-      // the same location on the display
-      double panX, panY;
-      view.GetPan(panX, panY);
-
-      int tx, ty;
-      view.MapSceneToDisplay(tx, ty, centerX_, centerY_);
-      view.SetPan(panX + static_cast<double>(downX_ - tx),
-                  panY + static_cast<double>(downY_ - ty));
-        
-      that_.SetView(view);
-    }
-  }
-}
--- a/Framework/Widgets/ZoomMouseTracker.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "WorldSceneWidget.h"
-
-namespace OrthancStone
-{
-  class ZoomMouseTracker : public IWorldSceneMouseTracker
-  {
-  private:
-    WorldSceneWidget&  that_;
-    double             originalZoom_;
-    int                downX_;
-    int                downY_;
-    double             centerX_;
-    double             centerY_;
-    bool               idle_;
-    double             normalization_;
-    
-  public:
-    ZoomMouseTracker(WorldSceneWidget& that,
-                     int x,
-                     int y);
-    
-    virtual bool HasRender() const
-    {
-      return false;
-    }
-
-    virtual void MouseUp()
-    {
-    }
-
-    virtual void Render(CairoContext& context,
-                        double zoom);
-
-    virtual void MouseMove(int displayX,
-                           int displayY,
-                           double x,
-                           double y,
-                           const std::vector<Touch>& displayTouches,
-                           const std::vector<Touch>& sceneTouches);
-  };
-}
--- a/Framework/dev.h	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,958 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2019 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU Affero General Public License
- * as published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "Layers/FrameRenderer.h"
-#include "Layers/LineLayerRenderer.h"
-#include "Layers/SliceOutlineRenderer.h"
-#include "Toolbox/DownloadStack.h"
-#include "Toolbox/GeometryToolbox.h"
-#include "Toolbox/OrthancSlicesLoader.h"
-#include "Volumes/ImageBuffer3D.h"
-#include "Volumes/ISlicedVolume.h"
-#include "Widgets/SliceViewerWidget.h"
-
-#include <Core/Logging.h>
-#include <Core/Images/ImageProcessing.h>
-#include <Core/OrthancException.h>
-
-#include <boost/math/special_functions/round.hpp>
-
-
-namespace OrthancStone
-{
-  // TODO: Handle errors while loading
-  class OrthancVolumeImage :
-    public ISlicedVolume,
-    public IObserver
-  {
-  private:
-    OrthancSlicesLoader           loader_;
-    std::auto_ptr<ImageBuffer3D>  image_;
-    std::auto_ptr<DownloadStack>  downloadStack_;
-    bool                          computeRange_;
-    size_t                        pendingSlices_;
-
-    void ScheduleSliceDownload()
-    {
-      assert(downloadStack_.get() != NULL);
-
-      unsigned int slice;
-      if (downloadStack_->Pop(slice))
-      {
-        loader_.ScheduleLoadSliceImage(slice, SliceImageQuality_Jpeg90);
-      }
-    }
-
-
-    static bool IsCompatible(const Deprecated::Slice& a,
-                             const Deprecated::Slice& b)
-    {
-      if (!GeometryToolbox::IsParallel(a.GetGeometry().GetNormal(),
-                                       b.GetGeometry().GetNormal()))
-      {
-        LOG(ERROR) << "A slice in the volume image is not parallel to the others.";
-        return false;
-      }
-
-      if (a.GetConverter().GetExpectedPixelFormat() != b.GetConverter().GetExpectedPixelFormat())
-      {
-        LOG(ERROR) << "The pixel format changes across the slices of the volume image.";
-        return false;
-      }
-
-      if (a.GetWidth() != b.GetWidth() ||
-          a.GetHeight() != b.GetHeight())
-      {
-        LOG(ERROR) << "The slices dimensions (width/height) are varying throughout the volume image";
-        return false;
-      }
-
-      if (!LinearAlgebra::IsNear(a.GetPixelSpacingX(), b.GetPixelSpacingX()) ||
-          !LinearAlgebra::IsNear(a.GetPixelSpacingY(), b.GetPixelSpacingY()))
-      {
-        LOG(ERROR) << "The pixel spacing of the slices change across the volume image";
-        return false;
-      }
-
-      return true;
-    }
-
-
-    static double GetDistance(const Deprecated::Slice& a,
-                              const Deprecated::Slice& b)
-    {
-      return fabs(a.GetGeometry().ProjectAlongNormal(a.GetGeometry().GetOrigin()) -
-                  a.GetGeometry().ProjectAlongNormal(b.GetGeometry().GetOrigin()));
-    }
-
-
-    void OnSliceGeometryReady(const OrthancSlicesLoader::SliceGeometryReadyMessage& message)
-    {
-      assert(&message.GetOrigin() == &loader_);
-
-      if (loader_.GetSlicesCount() == 0)
-      {
-        LOG(ERROR) << "Empty volume image";
-        BroadcastMessage(ISlicedVolume::GeometryErrorMessage(*this));
-        return;
-      }
-
-      for (size_t i = 1; i < loader_.GetSlicesCount(); i++)
-      {
-        if (!IsCompatible(loader_.GetSlice(0), loader_.GetSlice(i)))
-        {
-          BroadcastMessage(ISlicedVolume::GeometryErrorMessage(*this));
-          return;
-        }
-      }
-
-      double spacingZ;
-
-      if (loader_.GetSlicesCount() > 1)
-      {
-        spacingZ = GetDistance(loader_.GetSlice(0), loader_.GetSlice(1));
-      }
-      else
-      {
-        // This is a volume with one single slice: Choose a dummy
-        // z-dimension for voxels
-        spacingZ = 1;
-      }
-
-      for (size_t i = 1; i < loader_.GetSlicesCount(); i++)
-      {
-        if (!LinearAlgebra::IsNear(spacingZ, GetDistance(loader_.GetSlice(i - 1), loader_.GetSlice(i)),
-                                   0.001 /* this is expressed in mm */))
-        {
-          LOG(ERROR) << "The distance between successive slices is not constant in a volume image";
-          BroadcastMessage(ISlicedVolume::GeometryErrorMessage(*this));
-          return;
-        }
-      }
-
-      unsigned int width = loader_.GetSlice(0).GetWidth();
-      unsigned int height = loader_.GetSlice(0).GetHeight();
-      Orthanc::PixelFormat format = loader_.GetSlice(0).GetConverter().GetExpectedPixelFormat();
-      LOG(INFO) << "Creating a volume image of size " << width << "x" << height
-                << "x" << loader_.GetSlicesCount() << " in " << Orthanc::EnumerationToString(format);
-
-      image_.reset(new ImageBuffer3D(format, width, height, static_cast<unsigned int>(loader_.GetSlicesCount()), computeRange_));
-      image_->GetGeometry().SetAxialGeometry(loader_.GetSlice(0).GetGeometry());
-      image_->GetGeometry().SetVoxelDimensions(loader_.GetSlice(0).GetPixelSpacingX(),
-                                               loader_.GetSlice(0).GetPixelSpacingY(), spacingZ);
-      image_->Clear();
-
-      downloadStack_.reset(new DownloadStack(static_cast<unsigned int>(loader_.GetSlicesCount())));
-      pendingSlices_ = loader_.GetSlicesCount();
-
-      for (unsigned int i = 0; i < 4; i++)  // Limit to 4 simultaneous downloads
-      {
-        ScheduleSliceDownload();
-      }
-
-      // TODO Check the DicomFrameConverter are constant
-
-      BroadcastMessage(ISlicedVolume::GeometryReadyMessage(*this));
-    }
-
-
-    void OnSliceGeometryError(const OrthancSlicesLoader::SliceGeometryErrorMessage& message)
-    {
-      assert(&message.GetOrigin() == &loader_);
-
-      LOG(ERROR) << "Unable to download a volume image";
-      BroadcastMessage(ISlicedVolume::GeometryErrorMessage(*this));
-    }
-
-
-    void OnSliceImageReady(const OrthancSlicesLoader::SliceImageReadyMessage& message)
-    {
-      assert(&message.GetOrigin() == &loader_);
-
-      {
-        ImageBuffer3D::SliceWriter writer(*image_, VolumeProjection_Axial, message.GetSliceIndex());
-        Orthanc::ImageProcessing::Copy(writer.GetAccessor(), message.GetImage());
-      }
-
-      BroadcastMessage(ISlicedVolume::SliceContentChangedMessage
-                  (*this, message.GetSliceIndex(), message.GetSlice()));
-
-      if (pendingSlices_ == 1)
-      {
-        BroadcastMessage(ISlicedVolume::VolumeReadyMessage(*this));
-        pendingSlices_ = 0;
-      }
-      else if (pendingSlices_ > 1)
-      {
-        pendingSlices_ -= 1;
-      }
-
-      ScheduleSliceDownload();
-    }
-
-
-    void OnSliceImageError(const OrthancSlicesLoader::SliceImageErrorMessage& message)
-    {
-      assert(&message.GetOrigin() == &loader_);
-
-      LOG(ERROR) << "Cannot download slice " << message.GetSliceIndex() << " in a volume image";
-      ScheduleSliceDownload();
-    }
-
-
-  public:
-    OrthancVolumeImage(MessageBroker& broker,
-                       OrthancApiClient& orthanc,
-                       bool computeRange) :
-      ISlicedVolume(broker),
-      IObserver(broker),
-      loader_(broker, orthanc),
-      computeRange_(computeRange),
-      pendingSlices_(0)
-    {
-      loader_.RegisterObserverCallback(
-        new Callable<OrthancVolumeImage, OrthancSlicesLoader::SliceGeometryReadyMessage>
-        (*this, &OrthancVolumeImage::OnSliceGeometryReady));
-
-      loader_.RegisterObserverCallback(
-        new Callable<OrthancVolumeImage, OrthancSlicesLoader::SliceGeometryErrorMessage>
-        (*this, &OrthancVolumeImage::OnSliceGeometryError));
-
-      loader_.RegisterObserverCallback(
-        new Callable<OrthancVolumeImage, OrthancSlicesLoader::SliceImageReadyMessage>
-        (*this, &OrthancVolumeImage::OnSliceImageReady));
-
-      loader_.RegisterObserverCallback(
-        new Callable<OrthancVolumeImage, OrthancSlicesLoader::SliceImageErrorMessage>
-        (*this, &OrthancVolumeImage::OnSliceImageError));
-    }
-
-    void ScheduleLoadSeries(const std::string& seriesId)
-    {
-      loader_.ScheduleLoadSeries(seriesId);
-    }
-
-    void ScheduleLoadInstance(const std::string& instanceId)
-    {
-      loader_.ScheduleLoadInstance(instanceId);
-    }
-
-    void ScheduleLoadFrame(const std::string& instanceId,
-                           unsigned int frame)
-    {
-      loader_.ScheduleLoadFrame(instanceId, frame);
-    }
-
-    virtual size_t GetSlicesCount() const
-    {
-      return loader_.GetSlicesCount();
-    }
-
-    virtual const Deprecated::Slice& GetSlice(size_t index) const
-    {
-      return loader_.GetSlice(index);
-    }
-
-    ImageBuffer3D& GetImage() const
-    {
-      if (image_.get() == NULL)
-      {
-        // The geometry is not ready yet
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-      }
-      else
-      {
-        return *image_;
-      }
-    }
-
-    bool FitWindowingToRange(RenderStyle& style,
-                             const Deprecated::DicomFrameConverter& converter) const
-    {
-      if (image_.get() == NULL)
-      {
-        return false;
-      }
-      else
-      {
-        return image_->FitWindowingToRange(style, converter);
-      }
-    }
-  };
-
-
-  class OLD_VolumeImageGeometry
-  {
-  private:
-    unsigned int         width_;
-    unsigned int         height_;
-    size_t               depth_;
-    double               pixelSpacingX_;
-    double               pixelSpacingY_;
-    double               sliceThickness_;
-    CoordinateSystem3D   reference_;
-    Deprecated::DicomFrameConverter  converter_;
-
-    double ComputeAxialThickness(const OrthancVolumeImage& volume) const
-    {
-      double thickness;
-
-      size_t n = volume.GetSlicesCount();
-      if (n > 1)
-      {
-        const Deprecated::Slice& a = volume.GetSlice(0);
-        const Deprecated::Slice& b = volume.GetSlice(n - 1);
-        thickness = ((reference_.ProjectAlongNormal(b.GetGeometry().GetOrigin()) -
-                      reference_.ProjectAlongNormal(a.GetGeometry().GetOrigin())) /
-                     (static_cast<double>(n) - 1.0));
-      }
-      else
-      {
-        thickness = volume.GetSlice(0).GetThickness();
-      }
-
-      if (thickness <= 0)
-      {
-        // The slices should have been sorted with increasing Z
-        // (along the normal) by the OrthancSlicesLoader
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
-      }
-      else
-      {
-        return thickness;
-      }
-    }
-
-    void SetupAxial(const OrthancVolumeImage& volume)
-    {
-      const Deprecated::Slice& axial = volume.GetSlice(0);
-
-      width_ = axial.GetWidth();
-      height_ = axial.GetHeight();
-      depth_ = volume.GetSlicesCount();
-
-      pixelSpacingX_ = axial.GetPixelSpacingX();
-      pixelSpacingY_ = axial.GetPixelSpacingY();
-      sliceThickness_ = ComputeAxialThickness(volume);
-
-      reference_ = axial.GetGeometry();
-    }
-
-    void SetupCoronal(const OrthancVolumeImage& volume)
-    {
-      const Deprecated::Slice& axial = volume.GetSlice(0);
-      double axialThickness = ComputeAxialThickness(volume);
-
-      width_ = axial.GetWidth();
-      height_ = static_cast<unsigned int>(volume.GetSlicesCount());
-      depth_ = axial.GetHeight();
-
-      pixelSpacingX_ = axial.GetPixelSpacingX();
-      pixelSpacingY_ = axialThickness;
-      sliceThickness_ = axial.GetPixelSpacingY();
-
-      Vector origin = axial.GetGeometry().GetOrigin();
-      origin += (static_cast<double>(volume.GetSlicesCount() - 1) *
-                axialThickness * axial.GetGeometry().GetNormal());
-
-      reference_ = CoordinateSystem3D(origin,
-                                      axial.GetGeometry().GetAxisX(),
-                                      - axial.GetGeometry().GetNormal());
-    }
-
-    void SetupSagittal(const OrthancVolumeImage& volume)
-    {
-      const Deprecated::Slice& axial = volume.GetSlice(0);
-      double axialThickness = ComputeAxialThickness(volume);
-
-      width_ = axial.GetHeight();
-      height_ = static_cast<unsigned int>(volume.GetSlicesCount());
-      depth_ = axial.GetWidth();
-
-      pixelSpacingX_ = axial.GetPixelSpacingY();
-      pixelSpacingY_ = axialThickness;
-      sliceThickness_ = axial.GetPixelSpacingX();
-
-      Vector origin = axial.GetGeometry().GetOrigin();
-      origin += (static_cast<double>(volume.GetSlicesCount() - 1) *
-                axialThickness * axial.GetGeometry().GetNormal());
-
-      reference_ = CoordinateSystem3D(origin,
-                                      axial.GetGeometry().GetAxisY(),
-                                      axial.GetGeometry().GetNormal());
-    }
-
-  public:
-    OLD_VolumeImageGeometry(const OrthancVolumeImage& volume,
-                            VolumeProjection projection)
-    {
-      if (volume.GetSlicesCount() == 0)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-      }
-
-      converter_ = volume.GetSlice(0).GetConverter();
-
-      switch (projection)
-      {
-      case VolumeProjection_Axial:
-        SetupAxial(volume);
-        break;
-
-      case VolumeProjection_Coronal:
-        SetupCoronal(volume);
-        break;
-
-      case VolumeProjection_Sagittal:
-        SetupSagittal(volume);
-        break;
-
-      default:
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-      }
-    }
-
-    size_t GetSlicesCount() const
-    {
-      return depth_;
-    }
-
-    const Vector& GetNormal() const
-    {
-      return reference_.GetNormal();
-    }
-
-    bool LookupSlice(size_t& index,
-                     const CoordinateSystem3D& slice) const
-    {
-      bool opposite;
-      if (!GeometryToolbox::IsParallelOrOpposite(opposite,
-                                                 reference_.GetNormal(),
-                                                 slice.GetNormal()))
-      {
-        return false;
-      }
-
-      double z = (reference_.ProjectAlongNormal(slice.GetOrigin()) -
-                  reference_.ProjectAlongNormal(reference_.GetOrigin())) / sliceThickness_;
-
-      int s = static_cast<int>(boost::math::iround(z));
-
-      if (s < 0 ||
-        s >= static_cast<int>(depth_))
-      {
-        return false;
-      }
-      else
-      {
-        index = static_cast<size_t>(s);
-        return true;
-      }
-    }
-
-    Deprecated::Slice* GetSlice(size_t slice) const
-    {
-      if (slice >= depth_)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-      }
-      else
-      {
-        CoordinateSystem3D origin(reference_.GetOrigin() +
-                                  static_cast<double>(slice) * sliceThickness_ * reference_.GetNormal(),
-                                  reference_.GetAxisX(),
-                                  reference_.GetAxisY());
-
-        return new Deprecated::Slice(origin, pixelSpacingX_, pixelSpacingY_, sliceThickness_,
-                                     width_, height_, converter_);
-      }
-    }
-  };
-
-
-
-  class VolumeImageMPRSlicer :
-    public IVolumeSlicer,
-    public IObserver
-  {
-  private:
-    class RendererFactory : public LayerReadyMessage::IRendererFactory
-    {
-    private:
-      const Orthanc::ImageAccessor&  frame_;
-      const Deprecated::Slice&                   slice_;
-      bool                           isFullQuality_;
-
-    public:
-      RendererFactory(const Orthanc::ImageAccessor& frame,
-                      const Deprecated::Slice& slice,
-                      bool isFullQuality) :
-                      frame_(frame),
-                      slice_(slice),
-                      isFullQuality_(isFullQuality)
-      {
-      }
-
-      virtual ILayerRenderer* CreateRenderer() const
-      {
-        return FrameRenderer::CreateRenderer(frame_, slice_, isFullQuality_);
-      }
-    };
-
-
-    OrthancVolumeImage&                 volume_;
-    std::auto_ptr<OLD_VolumeImageGeometry>  axialGeometry_;
-    std::auto_ptr<OLD_VolumeImageGeometry>  coronalGeometry_;
-    std::auto_ptr<OLD_VolumeImageGeometry>  sagittalGeometry_;
-
-
-    bool IsGeometryReady() const
-    {
-      return axialGeometry_.get() != NULL;
-    }
-
-    void OnGeometryReady(const ISlicedVolume::GeometryReadyMessage& message)
-    {
-      assert(&message.GetOrigin() == &volume_);
-
-      // These 3 values are only used to speed up the IVolumeSlicer
-      axialGeometry_.reset(new OLD_VolumeImageGeometry(volume_, VolumeProjection_Axial));
-      coronalGeometry_.reset(new OLD_VolumeImageGeometry(volume_, VolumeProjection_Coronal));
-      sagittalGeometry_.reset(new OLD_VolumeImageGeometry(volume_, VolumeProjection_Sagittal));
-
-      BroadcastMessage(IVolumeSlicer::GeometryReadyMessage(*this));
-    }
-
-    void OnGeometryError(const ISlicedVolume::GeometryErrorMessage& message)
-    {
-      assert(&message.GetOrigin() == &volume_);
-
-      BroadcastMessage(IVolumeSlicer::GeometryErrorMessage(*this));
-    }
-
-    void OnContentChanged(const ISlicedVolume::ContentChangedMessage& message)
-    {
-      assert(&message.GetOrigin() == &volume_);
-
-      BroadcastMessage(IVolumeSlicer::ContentChangedMessage(*this));
-    }
-
-    void OnSliceContentChanged(const ISlicedVolume::SliceContentChangedMessage& message)
-    {
-      assert(&message.GetOrigin() == &volume_);
-
-      //IVolumeSlicer::OnSliceContentChange(slice);
-
-      // TODO Improve this?
-      BroadcastMessage(IVolumeSlicer::ContentChangedMessage(*this));
-    }
-
-    const OLD_VolumeImageGeometry& GetProjectionGeometry(VolumeProjection projection)
-    {
-      if (!IsGeometryReady())
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-      }
-
-      switch (projection)
-      {
-      case VolumeProjection_Axial:
-        return *axialGeometry_;
-
-      case VolumeProjection_Sagittal:
-        return *sagittalGeometry_;
-
-      case VolumeProjection_Coronal:
-        return *coronalGeometry_;
-
-      default:
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-      }
-    }
-
-
-    bool DetectProjection(VolumeProjection& projection,
-                          const CoordinateSystem3D& viewportSlice)
-    {
-      bool isOpposite;  // Ignored
-
-      if (GeometryToolbox::IsParallelOrOpposite(isOpposite,
-                                                viewportSlice.GetNormal(),
-                                                axialGeometry_->GetNormal()))
-      {
-        projection = VolumeProjection_Axial;
-        return true;
-      }
-      else if (GeometryToolbox::IsParallelOrOpposite(isOpposite,
-                                                     viewportSlice.GetNormal(),
-                                                     sagittalGeometry_->GetNormal()))
-      {
-        projection = VolumeProjection_Sagittal;
-        return true;
-      }
-      else if (GeometryToolbox::IsParallelOrOpposite(isOpposite,
-                                                     viewportSlice.GetNormal(),
-                                                     coronalGeometry_->GetNormal()))
-      {
-        projection = VolumeProjection_Coronal;
-        return true;
-      }
-      else
-      {
-        return false;
-      }
-    }
-
-
-  public:
-    VolumeImageMPRSlicer(MessageBroker& broker,
-                         OrthancVolumeImage&  volume) :
-                         IVolumeSlicer(broker),
-                         IObserver(broker),
-                         volume_(volume)
-    {
-      volume_.RegisterObserverCallback(
-        new Callable<VolumeImageMPRSlicer, ISlicedVolume::GeometryReadyMessage>
-        (*this, &VolumeImageMPRSlicer::OnGeometryReady));
-
-      volume_.RegisterObserverCallback(
-        new Callable<VolumeImageMPRSlicer, ISlicedVolume::GeometryErrorMessage>
-        (*this, &VolumeImageMPRSlicer::OnGeometryError));
-
-      volume_.RegisterObserverCallback(
-        new Callable<VolumeImageMPRSlicer, ISlicedVolume::ContentChangedMessage>
-        (*this, &VolumeImageMPRSlicer::OnContentChanged));
-
-      volume_.RegisterObserverCallback(
-        new Callable<VolumeImageMPRSlicer, ISlicedVolume::SliceContentChangedMessage>
-        (*this, &VolumeImageMPRSlicer::OnSliceContentChanged));
-    }
-
-    virtual bool GetExtent(std::vector<Vector>& points,
-                           const CoordinateSystem3D& viewportSlice) ORTHANC_OVERRIDE
-    {
-      VolumeProjection projection;
-
-      if (!IsGeometryReady() ||
-          !DetectProjection(projection, viewportSlice))
-      {
-        return false;
-      }
-      else
-      {
-        // As the slices of the volumic image are arranged in a box,
-        // we only consider one single reference slice (the one with index 0).
-        std::auto_ptr<Deprecated::Slice> slice(GetProjectionGeometry(projection).GetSlice(0));
-        slice->GetExtent(points);
-
-        return true;
-      }
-    }
-
-    virtual void ScheduleLayerCreation(const CoordinateSystem3D& viewportSlice) ORTHANC_OVERRIDE
-    {
-      VolumeProjection projection;
-
-      if (IsGeometryReady() &&
-          DetectProjection(projection, viewportSlice))
-      {
-        const OLD_VolumeImageGeometry& geometry = GetProjectionGeometry(projection);
-
-        size_t closest;
-
-        if (geometry.LookupSlice(closest, viewportSlice))
-        {
-          bool isFullQuality = true;  // TODO
-
-          std::auto_ptr<Orthanc::Image> frame;
-
-          {
-            ImageBuffer3D::SliceReader reader(volume_.GetImage(), projection, static_cast<unsigned int>(closest));
-
-            // TODO Transfer ownership if non-axial, to avoid memcpy
-            frame.reset(Orthanc::Image::Clone(reader.GetAccessor()));
-          }
-
-          std::auto_ptr<Deprecated::Slice> slice(geometry.GetSlice(closest));
-
-          RendererFactory factory(*frame, *slice, isFullQuality);
-
-          BroadcastMessage(IVolumeSlicer::LayerReadyMessage(*this, factory, slice->GetGeometry()));
-          return;
-        }
-      }
-
-      // Error
-      CoordinateSystem3D slice;
-      BroadcastMessage(IVolumeSlicer::LayerErrorMessage(*this, slice));
-    }
-  };
-
-
-  class VolumeImageInteractor :
-    public IWorldSceneInteractor,
-    public IObserver
-  {
-  private:
-    SliceViewerWidget&                  widget_;
-    VolumeProjection                    projection_;
-    std::auto_ptr<OLD_VolumeImageGeometry>  slices_;
-    size_t                              slice_;
-
-  protected:
-    void OnGeometryReady(const ISlicedVolume::GeometryReadyMessage& message)
-    {
-      if (slices_.get() == NULL)
-      {
-        const OrthancVolumeImage& image =
-          dynamic_cast<const OrthancVolumeImage&>(message.GetOrigin());
-
-        slices_.reset(new OLD_VolumeImageGeometry(image, projection_));
-        SetSlice(slices_->GetSlicesCount() / 2);
-
-        widget_.FitContent();
-      }
-    }
-
-    virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget,
-                                                        const ViewportGeometry& view,
-                                                        MouseButton button,
-                                                        KeyboardModifiers modifiers,
-                                                        int viewportX,
-                                                        int viewportY,
-                                                        double x,
-                                                        double y,
-                                                        IStatusBar* statusBar,
-                                                        const std::vector<Touch>& touches) ORTHANC_OVERRIDE
-    {
-      return  NULL;
-    }
-
-    virtual void MouseOver(CairoContext& context,
-                           WorldSceneWidget& widget,
-                           const ViewportGeometry& view,
-                           double x,
-                           double y,
-                           IStatusBar* statusBar) ORTHANC_OVERRIDE
-    {
-    }
-
-    virtual void MouseWheel(WorldSceneWidget& widget,
-                            MouseWheelDirection direction,
-                            KeyboardModifiers modifiers,
-                            IStatusBar* statusBar) ORTHANC_OVERRIDE
-    {
-      int scale = (modifiers & KeyboardModifiers_Control ? 10 : 1);
-
-      switch (direction)
-      {
-      case MouseWheelDirection_Up:
-        OffsetSlice(-scale);
-        break;
-
-      case MouseWheelDirection_Down:
-        OffsetSlice(scale);
-        break;
-
-      default:
-        break;
-      }
-    }
-
-    virtual void KeyPressed(WorldSceneWidget& widget,
-                            KeyboardKeys key,
-                            char keyChar,
-                            KeyboardModifiers modifiers,
-                            IStatusBar* statusBar) ORTHANC_OVERRIDE
-    {
-      switch (keyChar)
-      {
-      case 's':
-        widget.FitContent();
-        break;
-
-      default:
-        break;
-      }
-    }
-
-  public:
-    VolumeImageInteractor(MessageBroker& broker,
-                          OrthancVolumeImage& volume,
-                          SliceViewerWidget& widget,
-                          VolumeProjection projection) :
-                          IObserver(broker),
-                          widget_(widget),
-                          projection_(projection)
-    {
-      widget.SetInteractor(*this);
-
-      volume.RegisterObserverCallback(
-        new Callable<VolumeImageInteractor, ISlicedVolume::GeometryReadyMessage>
-        (*this, &VolumeImageInteractor::OnGeometryReady));
-    }
-
-    bool IsGeometryReady() const
-    {
-      return slices_.get() != NULL;
-    }
-
-    size_t GetSlicesCount() const
-    {
-      if (slices_.get() == NULL)
-      {
-        return 0;
-      }
-      else
-      {
-        return slices_->GetSlicesCount();
-      }
-    }
-
-    void OffsetSlice(int offset)
-    {
-      if (slices_.get() != NULL)
-      {
-        int slice = static_cast<int>(slice_) + offset;
-
-        if (slice < 0)
-        {
-          slice = 0;
-        }
-
-        if (slice >= static_cast<int>(slices_->GetSlicesCount()))
-        {
-          slice = static_cast<unsigned int>(slices_->GetSlicesCount()) - 1;
-        }
-
-        if (slice != static_cast<int>(slice_))
-        {
-          SetSlice(slice);
-        }
-      }
-    }
-
-    void SetSlice(size_t slice)
-    {
-      if (slices_.get() != NULL)
-      {
-        slice_ = slice;
-
-        std::auto_ptr<Deprecated::Slice> tmp(slices_->GetSlice(slice_));
-        widget_.SetSlice(tmp->GetGeometry());
-      }
-    }
-  };
-
-
-
-  class ReferenceLineSource : public IVolumeSlicer
-  {
-  private:
-    class RendererFactory : public LayerReadyMessage::IRendererFactory
-    {
-    private:
-      double                     x1_;
-      double                     y1_;
-      double                     x2_;
-      double                     y2_;
-      const CoordinateSystem3D&  slice_;
-
-    public:
-      RendererFactory(double x1,
-                      double y1,
-                      double x2,
-                      double y2,
-                      const CoordinateSystem3D& slice) :
-        x1_(x1),
-        y1_(y1),
-        x2_(x2),
-        y2_(y2),
-        slice_(slice)
-      {
-      }
-
-      virtual ILayerRenderer* CreateRenderer() const
-      {
-        return new LineLayerRenderer(x1_, y1_, x2_, y2_, slice_);
-      }
-    };
-
-    SliceViewerWidget&  otherPlane_;
-
-  public:
-    ReferenceLineSource(MessageBroker& broker,
-                        SliceViewerWidget&  otherPlane) :
-      IVolumeSlicer(broker),
-      otherPlane_(otherPlane)
-    {
-      BroadcastMessage(IVolumeSlicer::GeometryReadyMessage(*this));
-    }
-
-    virtual bool GetExtent(std::vector<Vector>& points,
-                           const CoordinateSystem3D& viewportSlice)
-    {
-      return false;
-    }
-
-    virtual void ScheduleLayerCreation(const CoordinateSystem3D& viewportSlice)
-    {
-      Deprecated::Slice reference(viewportSlice, 0.001);
-
-      Vector p, d;
-
-      const CoordinateSystem3D& slice = otherPlane_.GetSlice();
-
-      // Compute the line of intersection between the two slices
-      if (!GeometryToolbox::IntersectTwoPlanes(p, d,
-                                               slice.GetOrigin(), slice.GetNormal(),
-                                               viewportSlice.GetOrigin(), viewportSlice.GetNormal()))
-      {
-        // The two slice are parallel, don't try and display the intersection
-        BroadcastMessage(IVolumeSlicer::LayerErrorMessage(*this, reference.GetGeometry()));
-      }
-      else
-      {
-        double x1, y1, x2, y2;
-        viewportSlice.ProjectPoint(x1, y1, p);
-        viewportSlice.ProjectPoint(x2, y2, p + 1000.0 * d);
-
-        const Extent2D extent = otherPlane_.GetSceneExtent();
-
-        if (GeometryToolbox::ClipLineToRectangle(x1, y1, x2, y2,
-                                                 x1, y1, x2, y2,
-                                                 extent.GetX1(), extent.GetY1(),
-                                                 extent.GetX2(), extent.GetY2()))
-        {
-          RendererFactory factory(x1, y1, x2, y2, slice);
-          BroadcastMessage(IVolumeSlicer::LayerReadyMessage(*this, factory, reference.GetGeometry()));
-        }
-        else
-        {
-          // Error: Parallel slices
-          BroadcastMessage(IVolumeSlicer::LayerErrorMessage(*this, reference.GetGeometry()));
-        }
-      }
-    }
-  };
-}
--- a/Platforms/Generic/DelayedCallCommand.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Platforms/Generic/DelayedCallCommand.cpp	Wed May 22 16:13:46 2019 +0200
@@ -24,13 +24,13 @@
 
 #include <iostream>
 
-namespace OrthancStone
+namespace Deprecated
 {
-  DelayedCallCommand::DelayedCallCommand(MessageBroker& broker,
-                                         MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback,  // takes ownership
+  DelayedCallCommand::DelayedCallCommand(OrthancStone::MessageBroker& broker,
+                                         OrthancStone::MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback,  // takes ownership
                                          unsigned int timeoutInMs,
                                          Orthanc::IDynamicObject* payload /* takes ownership */,
-                                         NativeStoneApplicationContext& context
+                                         OrthancStone::NativeStoneApplicationContext& context
                                          ) :
     IObservable(broker),
     callback_(callback),
@@ -55,7 +55,7 @@
     // We want to make sure that, i.e, the UpdateThread is not
     // triggered while we are updating the "model" with the result of
     // an OracleCommand
-    NativeStoneApplicationContext::GlobalMutexLocker lock(context_);
+    OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker lock(context_);
 
     if (callback_.get() != NULL)
     {
--- a/Platforms/Generic/DelayedCallCommand.h	Wed May 22 16:01:34 2019 +0200
+++ b/Platforms/Generic/DelayedCallCommand.h	Wed May 22 16:13:46 2019 +0200
@@ -23,30 +23,30 @@
 
 #include "IOracleCommand.h"
 
-#include "../../Framework/Toolbox/IDelayedCallExecutor.h"
+#include "../../Framework/Deprecated/Toolbox/IDelayedCallExecutor.h"
 #include "../../Framework/Messages/IObservable.h"
 #include "../../Framework/Messages/ICallable.h"
 #include "../../Applications/Generic/NativeStoneApplicationContext.h"
 
 #include <boost/date_time/posix_time/posix_time.hpp>
 
-namespace OrthancStone
+namespace Deprecated
 {
-  class DelayedCallCommand : public IOracleCommand, IObservable
+  class DelayedCallCommand : public IOracleCommand, OrthancStone::IObservable
   {
   protected:
-    std::auto_ptr<MessageHandler<IDelayedCallExecutor::TimeoutMessage> >  callback_;
+    std::auto_ptr<OrthancStone::MessageHandler<IDelayedCallExecutor::TimeoutMessage> >  callback_;
     std::auto_ptr<Orthanc::IDynamicObject>  payload_;
-    NativeStoneApplicationContext&          context_;
+    OrthancStone::NativeStoneApplicationContext&          context_;
     boost::posix_time::ptime                expirationTimePoint_;
     unsigned int                            timeoutInMs_;
 
   public:
-    DelayedCallCommand(MessageBroker& broker,
-                       MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback,  // takes ownership
+    DelayedCallCommand(OrthancStone::MessageBroker& broker,
+                       OrthancStone::MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback,  // takes ownership
                        unsigned int timeoutInMs,
                        Orthanc::IDynamicObject* payload /* takes ownership */,
-                       NativeStoneApplicationContext& context
+                       OrthancStone::NativeStoneApplicationContext& context
                        );
 
     virtual void Execute();
--- a/Platforms/Generic/IOracleCommand.h	Wed May 22 16:01:34 2019 +0200
+++ b/Platforms/Generic/IOracleCommand.h	Wed May 22 16:13:46 2019 +0200
@@ -23,7 +23,7 @@
 
 #include <Core/IDynamicObject.h>
 
-namespace OrthancStone
+namespace Deprecated
 {
   class IOracleCommand : public Orthanc::IDynamicObject
   {
--- a/Platforms/Generic/Oracle.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Platforms/Generic/Oracle.cpp	Wed May 22 16:13:46 2019 +0200
@@ -29,7 +29,7 @@
 #include <stdio.h>
 #include <boost/thread/mutex.hpp>
 
-namespace OrthancStone
+namespace Deprecated
 {
   class Oracle::PImpl
   {
--- a/Platforms/Generic/Oracle.h	Wed May 22 16:01:34 2019 +0200
+++ b/Platforms/Generic/Oracle.h	Wed May 22 16:13:46 2019 +0200
@@ -25,7 +25,7 @@
 
 #include <boost/shared_ptr.hpp>
 
-namespace OrthancStone
+namespace Deprecated
 {
   class Oracle : public boost::noncopyable
   {
--- a/Platforms/Generic/OracleDelayedCallExecutor.h	Wed May 22 16:01:34 2019 +0200
+++ b/Platforms/Generic/OracleDelayedCallExecutor.h	Wed May 22 16:13:46 2019 +0200
@@ -21,31 +21,31 @@
 
 #pragma once
 
-#include "../../Framework/Toolbox/IDelayedCallExecutor.h"
+#include "../../Framework/Deprecated/Toolbox/IDelayedCallExecutor.h"
 #include "Oracle.h"
 #include "../../Applications/Generic/NativeStoneApplicationContext.h"
 #include "DelayedCallCommand.h"
 
-namespace OrthancStone
+namespace Deprecated
 {
   // The OracleTimeout executes callbacks after a delay.
   class OracleDelayedCallExecutor : public IDelayedCallExecutor
   {
   private:
     Oracle&                        oracle_;
-    NativeStoneApplicationContext& context_;
+    OrthancStone::NativeStoneApplicationContext& context_;
 
   public:
-    OracleDelayedCallExecutor(MessageBroker& broker,
+    OracleDelayedCallExecutor(OrthancStone::MessageBroker& broker,
                               Oracle& oracle,
-                              NativeStoneApplicationContext& context) :
+                              OrthancStone::NativeStoneApplicationContext& context) :
       IDelayedCallExecutor(broker),
       oracle_(oracle),
       context_(context)
     {
     }
 
-    virtual void Schedule(MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback,
+    virtual void Schedule(OrthancStone::MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback,
                           unsigned int timeoutInMs = 1000)
     {
       oracle_.Submit(new DelayedCallCommand(broker_, callback, timeoutInMs, NULL, context_));
--- a/Platforms/Generic/OracleWebService.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Platforms/Generic/OracleWebService.cpp	Wed May 22 16:13:46 2019 +0200
@@ -20,26 +20,26 @@
 
 
 #include "OracleWebService.h"
-#include "../../Framework/Toolbox/IWebService.h"
+#include "../../Framework/Deprecated/Toolbox/IWebService.h"
 
-namespace OrthancStone
+namespace Deprecated
 {
 
 
-  class OracleWebService::WebServiceCachedGetCommand : public IOracleCommand, IObservable
+  class OracleWebService::WebServiceCachedGetCommand : public IOracleCommand, OrthancStone::IObservable
   {
   protected:
-    std::auto_ptr<MessageHandler<IWebService::HttpRequestSuccessMessage> >  successCallback_;
+    std::auto_ptr<OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> >  successCallback_;
     std::auto_ptr<Orthanc::IDynamicObject>                                  payload_;
     boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage>      cachedMessage_;
-    NativeStoneApplicationContext&                                          context_;
+    OrthancStone::NativeStoneApplicationContext&                                          context_;
 
   public:
-    WebServiceCachedGetCommand(MessageBroker& broker,
-                               MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
+    WebServiceCachedGetCommand(OrthancStone::MessageBroker& broker,
+                               OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
                                boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedMessage,
                                Orthanc::IDynamicObject* payload /* takes ownership */,
-                               NativeStoneApplicationContext& context
+                               OrthancStone::NativeStoneApplicationContext& context
                                ) :
       IObservable(broker),
       successCallback_(successCallback),
@@ -59,7 +59,7 @@
       // We want to make sure that, i.e, the UpdateThread is not
       // triggered while we are updating the "model" with the result of
       // a WebServiceCommand
-      NativeStoneApplicationContext::GlobalMutexLocker lock(context_);
+      OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker lock(context_);
 
       IWebService::HttpRequestSuccessMessage successMessage(cachedMessage_->GetUri(),
                                                             cachedMessage_->GetAnswer(),
@@ -73,7 +73,7 @@
 
   void OracleWebService::NotifyHttpSuccessLater(boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedMessage,
                                                 Orthanc::IDynamicObject* payload, // takes ownership
-                                                MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback)
+                                                OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback)
   {
     oracle_.Submit(new WebServiceCachedGetCommand(GetBroker(), successCallback, cachedMessage, payload, context_));
   }
--- a/Platforms/Generic/OracleWebService.h	Wed May 22 16:01:34 2019 +0200
+++ b/Platforms/Generic/OracleWebService.h	Wed May 22 16:13:46 2019 +0200
@@ -21,14 +21,14 @@
 
 #pragma once
 
-#include "../../Framework/Toolbox/BaseWebService.h"
+#include "../../Framework/Deprecated/Toolbox/BaseWebService.h"
 #include "Oracle.h"
 #include "WebServiceGetCommand.h"
 #include "WebServicePostCommand.h"
 #include "WebServiceDeleteCommand.h"
 #include "../../Applications/Generic/NativeStoneApplicationContext.h"
 
-namespace OrthancStone
+namespace Deprecated
 {
   // The OracleWebService performs HTTP requests in a native environment.
   // It uses a thread pool to handle multiple HTTP requests in a same time.
@@ -37,16 +37,16 @@
   {
   private:
     Oracle&                        oracle_;
-    NativeStoneApplicationContext& context_;
+    OrthancStone::NativeStoneApplicationContext& context_;
     Orthanc::WebServiceParameters  parameters_;
 
     class WebServiceCachedGetCommand;
 
   public:
-    OracleWebService(MessageBroker& broker,
+    OracleWebService(OrthancStone::MessageBroker& broker,
                      Oracle& oracle,
                      const Orthanc::WebServiceParameters& parameters,
-                     NativeStoneApplicationContext& context) :
+                     OrthancStone::NativeStoneApplicationContext& context) :
       BaseWebService(broker),
       oracle_(oracle),
       context_(context),
@@ -58,8 +58,8 @@
                            const HttpHeaders& headers,
                            const std::string& body,
                            Orthanc::IDynamicObject* payload, // takes ownership
-                           MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback, // takes ownership
-                           MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL, // takes ownership
+                           OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback, // takes ownership
+                           OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL, // takes ownership
                            unsigned int timeoutInSeconds = 60)
     {
       oracle_.Submit(new WebServicePostCommand(GetBroker(), successCallback, failureCallback, parameters_, uri, headers, timeoutInSeconds, body, payload, context_));
@@ -68,8 +68,8 @@
     virtual void DeleteAsync(const std::string& uri,
                              const HttpHeaders& headers,
                              Orthanc::IDynamicObject* payload,
-                             MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,
-                             MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
+                             OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,
+                             OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,
                              unsigned int timeoutInSeconds = 60)
     {
       oracle_.Submit(new WebServiceDeleteCommand(GetBroker(), successCallback, failureCallback, parameters_, uri, headers, timeoutInSeconds, payload, context_));
@@ -79,8 +79,8 @@
     virtual void GetAsyncInternal(const std::string& uri,
                                   const HttpHeaders& headers,
                                   Orthanc::IDynamicObject* payload, // takes ownership
-                                  MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,   // takes ownership
-                                  MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,// takes ownership
+                                  OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,   // takes ownership
+                                  OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback = NULL,// takes ownership
                                   unsigned int timeoutInSeconds = 60)
     {
       oracle_.Submit(new WebServiceGetCommand(GetBroker(), successCallback, failureCallback, parameters_, uri, headers, timeoutInSeconds, payload, context_));
@@ -88,7 +88,7 @@
 
     virtual void NotifyHttpSuccessLater(boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedHttpMessage,
                                         Orthanc::IDynamicObject* payload, // takes ownership
-                                        MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback);
+                                        OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback);
 
   };
 }
--- a/Platforms/Generic/WebServiceCommandBase.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Platforms/Generic/WebServiceCommandBase.cpp	Wed May 22 16:13:46 2019 +0200
@@ -23,17 +23,17 @@
 
 #include <Core/HttpClient.h>
 
-namespace OrthancStone
+namespace Deprecated
 {
-  WebServiceCommandBase::WebServiceCommandBase(MessageBroker& broker,
-                                               MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,
-                                               MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,
+  WebServiceCommandBase::WebServiceCommandBase(OrthancStone::MessageBroker& broker,
+                                               OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,
+                                               OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,
                                                const Orthanc::WebServiceParameters& parameters,
                                                const std::string& url,
                                                const IWebService::HttpHeaders& headers,
                                                unsigned int timeoutInSeconds,
                                                Orthanc::IDynamicObject* payload /* takes ownership */,
-                                               NativeStoneApplicationContext& context) :
+                                               OrthancStone::NativeStoneApplicationContext& context) :
     IObservable(broker),
     successCallback_(successCallback),
     failureCallback_(failureCallback),
@@ -52,7 +52,7 @@
     // We want to make sure that, i.e, the UpdateThread is not
     // triggered while we are updating the "model" with the result of
     // a WebServiceCommand
-    NativeStoneApplicationContext::GlobalMutexLocker lock(context_); 
+    OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker lock(context_); 
 
     if (success_ && successCallback_.get() != NULL)
     {
--- a/Platforms/Generic/WebServiceCommandBase.h	Wed May 22 16:01:34 2019 +0200
+++ b/Platforms/Generic/WebServiceCommandBase.h	Wed May 22 16:13:46 2019 +0200
@@ -23,7 +23,7 @@
 
 #include "IOracleCommand.h"
 
-#include "../../Framework/Toolbox/IWebService.h"
+#include "../../Framework/Deprecated/Toolbox/IWebService.h"
 #include "../../Framework/Messages/IObservable.h"
 #include "../../Framework/Messages/ICallable.h"
 #include "../../Applications/Generic/NativeStoneApplicationContext.h"
@@ -32,13 +32,13 @@
 
 #include <memory>
 
-namespace OrthancStone
+namespace Deprecated
 {
-  class WebServiceCommandBase : public IOracleCommand, IObservable
+  class WebServiceCommandBase : public IOracleCommand, OrthancStone::IObservable
   {
   protected:
-    std::auto_ptr<MessageHandler<IWebService::HttpRequestSuccessMessage> >  successCallback_;
-    std::auto_ptr<MessageHandler<IWebService::HttpRequestErrorMessage> >    failureCallback_;
+    std::auto_ptr<OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> >  successCallback_;
+    std::auto_ptr<OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage> >    failureCallback_;
     Orthanc::WebServiceParameters           parameters_;
     std::string                             url_;
     IWebService::HttpHeaders                headers_;
@@ -46,19 +46,19 @@
     bool                                    success_;
     std::string                             answer_;
     IWebService::HttpHeaders                answerHeaders_;
-    NativeStoneApplicationContext&          context_;
+    OrthancStone::NativeStoneApplicationContext&          context_;
     unsigned int                            timeoutInSeconds_;
 
   public:
-    WebServiceCommandBase(MessageBroker& broker,
-                          MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
-                          MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
+    WebServiceCommandBase(OrthancStone::MessageBroker& broker,
+                          OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
+                          OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
                           const Orthanc::WebServiceParameters& parameters,
                           const std::string& url,
                           const IWebService::HttpHeaders& headers,
                           unsigned int timeoutInSeconds,
                           Orthanc::IDynamicObject* payload /* takes ownership */,
-                          NativeStoneApplicationContext& context
+                          OrthancStone::NativeStoneApplicationContext& context
                           );
 
     virtual void Execute() = 0;
--- a/Platforms/Generic/WebServiceDeleteCommand.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Platforms/Generic/WebServiceDeleteCommand.cpp	Wed May 22 16:13:46 2019 +0200
@@ -23,17 +23,17 @@
 
 #include <Core/HttpClient.h>
 
-namespace OrthancStone
+namespace Deprecated
 {
-  WebServiceDeleteCommand::WebServiceDeleteCommand(MessageBroker& broker,
-                                                   MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
-                                                   MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
+  WebServiceDeleteCommand::WebServiceDeleteCommand(OrthancStone::MessageBroker& broker,
+                                                   OrthancStone::MessageHandler<Deprecated::IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
+                                                   OrthancStone::MessageHandler<Deprecated::IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
                                                    const Orthanc::WebServiceParameters& parameters,
                                                    const std::string& url,
-                                                   const IWebService::HttpHeaders& headers,
+                                                   const Deprecated::IWebService::HttpHeaders& headers,
                                                    unsigned int timeoutInSeconds,
                                                    Orthanc::IDynamicObject* payload /* takes ownership */,
-                                                   NativeStoneApplicationContext& context) :
+                                                   OrthancStone::NativeStoneApplicationContext& context) :
     WebServiceCommandBase(broker, successCallback, failureCallback, parameters, url, headers, timeoutInSeconds, payload, context)
   {
   }
@@ -45,7 +45,7 @@
     client.SetTimeout(timeoutInSeconds_);
     client.SetMethod(Orthanc::HttpMethod_Delete);
 
-    for (IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ )
+    for (Deprecated::IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ )
     {
       client.AddHeader(it->first, it->second);
     }
--- a/Platforms/Generic/WebServiceDeleteCommand.h	Wed May 22 16:01:34 2019 +0200
+++ b/Platforms/Generic/WebServiceDeleteCommand.h	Wed May 22 16:13:46 2019 +0200
@@ -23,20 +23,20 @@
 
 #include "WebServiceCommandBase.h"
 
-namespace OrthancStone
+namespace Deprecated
 {
   class WebServiceDeleteCommand : public WebServiceCommandBase
   {
   public:
-    WebServiceDeleteCommand(MessageBroker& broker,
-                            MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
-                            MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
+    WebServiceDeleteCommand(OrthancStone::MessageBroker& broker,
+                            OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
+                            OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
                             const Orthanc::WebServiceParameters& parameters,
                             const std::string& url,
                             const IWebService::HttpHeaders& headers,
                             unsigned int timeoutInSeconds,
                             Orthanc::IDynamicObject* payload /* takes ownership */,
-                            NativeStoneApplicationContext& context);
+                            OrthancStone::NativeStoneApplicationContext& context);
 
     virtual void Execute();
   };
--- a/Platforms/Generic/WebServiceGetCommand.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Platforms/Generic/WebServiceGetCommand.cpp	Wed May 22 16:13:46 2019 +0200
@@ -23,18 +23,18 @@
 
 #include <Core/HttpClient.h>
 
-namespace OrthancStone
+namespace Deprecated
 {
 
-  WebServiceGetCommand::WebServiceGetCommand(MessageBroker& broker,
-                                             MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
-                                             MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
+  WebServiceGetCommand::WebServiceGetCommand(OrthancStone::MessageBroker& broker,
+                                             OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
+                                             OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
                                              const Orthanc::WebServiceParameters& parameters,
                                              const std::string& url,
                                              const IWebService::HttpHeaders& headers,
                                              unsigned int timeoutInSeconds,
                                              Orthanc::IDynamicObject* payload /* takes ownership */,
-                                             NativeStoneApplicationContext& context) :
+                                             OrthancStone::NativeStoneApplicationContext& context) :
     WebServiceCommandBase(broker, successCallback, failureCallback, parameters, url, headers, timeoutInSeconds, payload, context)
   {
   }
--- a/Platforms/Generic/WebServiceGetCommand.h	Wed May 22 16:01:34 2019 +0200
+++ b/Platforms/Generic/WebServiceGetCommand.h	Wed May 22 16:13:46 2019 +0200
@@ -23,20 +23,20 @@
 
 #include "WebServiceCommandBase.h"
 
-namespace OrthancStone
+namespace Deprecated
 {
   class WebServiceGetCommand : public WebServiceCommandBase
   {
   public:
-    WebServiceGetCommand(MessageBroker& broker,
-                         MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
-                         MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
+    WebServiceGetCommand(OrthancStone::MessageBroker& broker,
+                         OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
+                         OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
                          const Orthanc::WebServiceParameters& parameters,
                          const std::string& url,
                          const IWebService::HttpHeaders& headers,
                          unsigned int timeoutInSeconds,
                          Orthanc::IDynamicObject* payload /* takes ownership */,
-                         NativeStoneApplicationContext& context);
+                         OrthancStone::NativeStoneApplicationContext& context);
 
     virtual void Execute();
   };
--- a/Platforms/Generic/WebServicePostCommand.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Platforms/Generic/WebServicePostCommand.cpp	Wed May 22 16:13:46 2019 +0200
@@ -23,18 +23,18 @@
 
 #include <Core/HttpClient.h>
 
-namespace OrthancStone
+namespace Deprecated
 {
-  WebServicePostCommand::WebServicePostCommand(MessageBroker& broker,
-                                               MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
-                                               MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
+  WebServicePostCommand::WebServicePostCommand(OrthancStone::MessageBroker& broker,
+                                               OrthancStone::MessageHandler<Deprecated::IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
+                                               OrthancStone::MessageHandler<Deprecated::IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
                                                const Orthanc::WebServiceParameters& parameters,
                                                const std::string& url,
-                                               const IWebService::HttpHeaders& headers,
+                                               const Deprecated::IWebService::HttpHeaders& headers,
                                                unsigned int timeoutInSeconds,
                                                const std::string& body,
                                                Orthanc::IDynamicObject* payload /* takes ownership */,
-                                               NativeStoneApplicationContext& context) :
+                                               OrthancStone::NativeStoneApplicationContext& context) :
     WebServiceCommandBase(broker, successCallback, failureCallback, parameters, url, headers, timeoutInSeconds, payload, context),
     body_(body)
   {
@@ -48,7 +48,7 @@
     client.SetMethod(Orthanc::HttpMethod_Post);
     client.GetBody().swap(body_);
 
-    for (IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ )
+    for (Deprecated::IWebService::HttpHeaders::const_iterator it = headers_.begin(); it != headers_.end(); it++ )
     {
       client.AddHeader(it->first, it->second);
     }
--- a/Platforms/Generic/WebServicePostCommand.h	Wed May 22 16:01:34 2019 +0200
+++ b/Platforms/Generic/WebServicePostCommand.h	Wed May 22 16:13:46 2019 +0200
@@ -23,7 +23,7 @@
 
 #include "WebServiceCommandBase.h"
 
-namespace OrthancStone
+namespace Deprecated
 {
   class WebServicePostCommand : public WebServiceCommandBase
   {
@@ -31,16 +31,16 @@
     std::string  body_;
 
   public:
-    WebServicePostCommand(MessageBroker& broker,
-                          MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
-                          MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
+    WebServicePostCommand(OrthancStone::MessageBroker& broker,
+                          OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback,  // takes ownership
+                          OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallback,  // takes ownership
                           const Orthanc::WebServiceParameters& parameters,
                           const std::string& url,
                           const IWebService::HttpHeaders& headers,
                           unsigned int timeoutInSeconds,
                           const std::string& body,
                           Orthanc::IDynamicObject* payload /* takes ownership */,
-                          NativeStoneApplicationContext& context);
+                          OrthancStone::NativeStoneApplicationContext& context);
 
     virtual void Execute();
   };
--- a/Platforms/Wasm/Defaults.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Platforms/Wasm/Defaults.cpp	Wed May 22 16:13:46 2019 +0200
@@ -2,9 +2,8 @@
 
 #include "WasmWebService.h"
 #include "WasmDelayedCallExecutor.h"
-#include <Framework/dev.h>
-#include "Framework/Widgets/TestCairoWidget.h"
-#include <Framework/Viewport/WidgetViewport.h>
+#include "../../Framework/Deprecated/Widgets/TestCairoWidget.h"
+#include <Framework/Deprecated/Viewport/WidgetViewport.h>
 #include <Applications/Wasm/StartupParametersBuilder.h>
 #include <Platforms/Wasm/WasmPlatformApplicationAdapter.h>
 #include <Core/Logging.h>
@@ -26,16 +25,16 @@
 static OrthancStone::ViewportContentChangedObserver viewportContentChangedObserver_(broker);
 static OrthancStone::StatusBar statusBar_;
 
-static std::list<std::shared_ptr<OrthancStone::WidgetViewport>> viewports_;
+static std::list<std::shared_ptr<Deprecated::WidgetViewport>> viewports_;
 
-std::shared_ptr<OrthancStone::WidgetViewport> FindViewportSharedPtr(ViewportHandle viewport) {
+std::shared_ptr<Deprecated::WidgetViewport> FindViewportSharedPtr(ViewportHandle viewport) {
   for (const auto& v : viewports_) {
     if (v.get() == viewport) {
       return v;
     }
   }
   assert(false);
-  return std::shared_ptr<OrthancStone::WidgetViewport>();
+  return std::shared_ptr<Deprecated::WidgetViewport>();
 }
 
 #ifdef __cplusplus
@@ -47,7 +46,7 @@
   // when WASM needs a C++ viewport
   ViewportHandle EMSCRIPTEN_KEEPALIVE CreateCppViewport() {
     
-    std::shared_ptr<OrthancStone::WidgetViewport> viewport(new OrthancStone::WidgetViewport(broker));
+    std::shared_ptr<Deprecated::WidgetViewport> viewport(new Deprecated::WidgetViewport(broker));
     printf("viewport %x\n", (int)viewport.get());
 
     viewports_.push_back(viewport);
@@ -57,7 +56,7 @@
     viewport->SetStatusBar(statusBar_);
 
     viewport->RegisterObserverCallback(
-      new Callable<ViewportContentChangedObserver, IViewport::ViewportChangedMessage>
+      new Callable<ViewportContentChangedObserver, Deprecated::IViewport::ViewportChangedMessage>
       (viewportContentChangedObserver_, &ViewportContentChangedObserver::OnViewportChanged));
 
     return viewport.get();
@@ -65,7 +64,7 @@
 
   // when WASM does not need a viewport anymore, it should release it 
   void EMSCRIPTEN_KEEPALIVE ReleaseCppViewport(ViewportHandle viewport) {
-    viewports_.remove_if([viewport](const std::shared_ptr<OrthancStone::WidgetViewport>& v) { return v.get() == viewport;});
+    viewports_.remove_if([viewport](const std::shared_ptr<Deprecated::WidgetViewport>& v) { return v.get() == viewport;});
 
     printf("There are now %lu viewports in C++\n", viewports_.size());
   }
@@ -76,8 +75,8 @@
 
     application.reset(CreateUserApplication(broker));
     applicationWasmAdapter.reset(CreateWasmApplicationAdapter(broker, application.get())); 
-    WasmWebService::SetBroker(broker);
-    WasmDelayedCallExecutor::SetBroker(broker);
+    Deprecated::WasmWebService::SetBroker(broker);
+    Deprecated::WasmDelayedCallExecutor::SetBroker(broker);
 
     startupParametersBuilder.Clear();
   }
@@ -104,8 +103,8 @@
     context.reset(new OrthancStone::StoneApplicationContext(broker));
     context->SetOrthancBaseUrl(baseUri);
     printf("Base URL to Orthanc API: [%s]\n", baseUri);
-    context->SetWebService(OrthancStone::WasmWebService::GetInstance());
-    context->SetDelayedCallExecutor(OrthancStone::WasmDelayedCallExecutor::GetInstance());
+    context->SetWebService(Deprecated::WasmWebService::GetInstance());
+    context->SetDelayedCallExecutor(Deprecated::WasmDelayedCallExecutor::GetInstance());
     application->Initialize(context.get(), statusBar_, parameters);
     application->InitializeWasm();
 
@@ -208,7 +207,7 @@
         return;  // Unknown button
     }
 
-    viewport->MouseDown(button, x, y, OrthancStone::KeyboardModifiers_None, std::vector<OrthancStone::Touch>());
+    viewport->MouseDown(button, x, y, OrthancStone::KeyboardModifiers_None, std::vector<Deprecated::Touch>());
   }
   
 
@@ -239,10 +238,10 @@
                                               int x,
                                               int y)
   {
-    viewport->MouseMove(x, y, std::vector<OrthancStone::Touch>());
+    viewport->MouseMove(x, y, std::vector<Deprecated::Touch>());
   }
 
-  void GetTouchVector(std::vector<OrthancStone::Touch>& output,
+  void GetTouchVector(std::vector<Deprecated::Touch>& output,
                       int touchCount,
                       float x0,
                       float y0,
@@ -254,15 +253,15 @@
     // TODO: it might be nice to try to pass all the x0,y0 coordinates as arrays but that's not so easy to pass array between JS and C++
     if (touchCount > 0)
     {
-      output.push_back(OrthancStone::Touch(x0, y0));
+      output.push_back(Deprecated::Touch(x0, y0));
     }
     if (touchCount > 1)
     {
-      output.push_back(OrthancStone::Touch(x1, y1));
+      output.push_back(Deprecated::Touch(x1, y1));
     }
     if (touchCount > 2)
     {
-      output.push_back(OrthancStone::Touch(x2, y2));
+      output.push_back(Deprecated::Touch(x2, y2));
     }
 
   }
@@ -278,7 +277,7 @@
   {
     printf("touch start with %d touches\n", touchCount);
 
-    std::vector<OrthancStone::Touch> touches;
+    std::vector<Deprecated::Touch> touches;
     GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2);
     viewport->TouchStart(touches);
   }
@@ -294,7 +293,7 @@
   {
     printf("touch move with %d touches\n", touchCount);
 
-    std::vector<OrthancStone::Touch> touches;
+    std::vector<Deprecated::Touch> touches;
     GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2);
     viewport->TouchMove(touches);
   }
@@ -310,7 +309,7 @@
   {
     printf("touch end with %d touches remaining\n", touchCount);
 
-    std::vector<OrthancStone::Touch> touches;
+    std::vector<Deprecated::Touch> touches;
     GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2);
     viewport->TouchEnd(touches);
   }
--- a/Platforms/Wasm/Defaults.h	Wed May 22 16:01:34 2019 +0200
+++ b/Platforms/Wasm/Defaults.h	Wed May 22 16:13:46 2019 +0200
@@ -2,13 +2,12 @@
 
 #include <emscripten/emscripten.h>
 
-#include <Framework/dev.h>
-#include <Framework/Viewport/WidgetViewport.h>
-#include <Framework/Widgets/LayoutWidget.h>
+#include "../../Framework/Deprecated/Viewport/WidgetViewport.h"
+#include "../../Framework/Deprecated/Widgets/LayoutWidget.h"
 #include <Applications/IStoneApplication.h>
 #include <Platforms/Wasm/WasmPlatformApplicationAdapter.h>
 
-typedef OrthancStone::WidgetViewport* ViewportHandle; // the objects exchanged between JS and C++
+typedef Deprecated::WidgetViewport* ViewportHandle; // the objects exchanged between JS and C++
 
 #ifdef __cplusplus
 extern "C" {
@@ -57,7 +56,7 @@
       isScheduled_ = false;
     }
 
-    void OnViewportChanged(const IViewport::ViewportChangedMessage& message)
+    void OnViewportChanged(const Deprecated::IViewport::ViewportChangedMessage& message)
     {
       if (!isScheduled_)
       {
@@ -68,7 +67,7 @@
   };
 
   // default status bar to log messages on the console/stdout
-  class StatusBar : public OrthancStone::IStatusBar
+  class StatusBar : public Deprecated::IStatusBar
   {
   public:
     virtual void ClearMessage()
--- a/Platforms/Wasm/WasmDelayedCallExecutor.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Platforms/Wasm/WasmDelayedCallExecutor.cpp	Wed May 22 16:13:46 2019 +0200
@@ -21,8 +21,8 @@
     }
     else
     {
-      reinterpret_cast<OrthancStone::MessageHandler<OrthancStone::IDelayedCallExecutor::TimeoutMessage>*>(callable)->
-        Apply(OrthancStone::IDelayedCallExecutor::TimeoutMessage()); // uri, reinterpret_cast<Orthanc::IDynamicObject*>(payload)));
+      reinterpret_cast<OrthancStone::MessageHandler<Deprecated::IDelayedCallExecutor::TimeoutMessage>*>(callable)->
+        Apply(Deprecated::IDelayedCallExecutor::TimeoutMessage()); // uri, reinterpret_cast<Orthanc::IDynamicObject*>(payload)));
     }
   }
 
@@ -33,12 +33,12 @@
 
 
 
-namespace OrthancStone
+namespace Deprecated
 {
-  MessageBroker* WasmDelayedCallExecutor::broker_ = NULL;
+  OrthancStone::MessageBroker* WasmDelayedCallExecutor::broker_ = NULL;
 
 
-  void WasmDelayedCallExecutor::Schedule(MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback,
+  void WasmDelayedCallExecutor::Schedule(OrthancStone::MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback,
                          unsigned int timeoutInMs)
   {
     WasmDelayedCallExecutor_Schedule(callback, timeoutInMs);
--- a/Platforms/Wasm/WasmDelayedCallExecutor.h	Wed May 22 16:01:34 2019 +0200
+++ b/Platforms/Wasm/WasmDelayedCallExecutor.h	Wed May 22 16:13:46 2019 +0200
@@ -1,17 +1,17 @@
 #pragma once
 
-#include <Framework/Toolbox/IDelayedCallExecutor.h>
+#include "../../Framework/Deprecated/Toolbox/IDelayedCallExecutor.h"
 #include <Core/OrthancException.h>
 
-namespace OrthancStone
+namespace Deprecated
 {
   class WasmDelayedCallExecutor : public IDelayedCallExecutor
   {
   private:
-    static MessageBroker* broker_;
+    static OrthancStone::MessageBroker* broker_;
 
     // Private constructor => Singleton design pattern
-    WasmDelayedCallExecutor(MessageBroker& broker) :
+    WasmDelayedCallExecutor(OrthancStone::MessageBroker& broker) :
       IDelayedCallExecutor(broker)
     {
     }
@@ -28,12 +28,12 @@
       return instance;
     }
 
-    static void SetBroker(MessageBroker& broker)
+    static void SetBroker(OrthancStone::MessageBroker& broker)
     {
       broker_ = &broker;
     }
 
-    virtual void Schedule(MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback,
+    virtual void Schedule(OrthancStone::MessageHandler<IDelayedCallExecutor::TimeoutMessage>* callback,
                          unsigned int timeoutInMs = 1000);
 
   };
--- a/Platforms/Wasm/WasmViewport.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Platforms/Wasm/WasmViewport.cpp	Wed May 22 16:13:46 2019 +0200
@@ -3,11 +3,11 @@
 #include <vector>
 #include <memory>
 
-std::vector<std::shared_ptr<OrthancStone::WidgetViewport>> wasmViewports;
+std::vector<std::shared_ptr<Deprecated::WidgetViewport>> wasmViewports;
 
-void AttachWidgetToWasmViewport(const char* htmlCanvasId, OrthancStone::IWidget* centralWidget) {
-    std::shared_ptr<OrthancStone::WidgetViewport> viewport(CreateWasmViewportFromCpp(htmlCanvasId));
+void AttachWidgetToWasmViewport(const char* htmlCanvasId, Deprecated::IWidget* centralWidget) {
+    std::shared_ptr<Deprecated::WidgetViewport> viewport(CreateWasmViewportFromCpp(htmlCanvasId));
     viewport->SetCentralWidget(centralWidget);
 
     wasmViewports.push_back(viewport);
-}
\ No newline at end of file
+}
--- a/Platforms/Wasm/WasmViewport.h	Wed May 22 16:01:34 2019 +0200
+++ b/Platforms/Wasm/WasmViewport.h	Wed May 22 16:13:46 2019 +0200
@@ -1,6 +1,6 @@
 #pragma once
 
-#include <Framework/Viewport/WidgetViewport.h>
+#include "../../Framework/Deprecated/Viewport/WidgetViewport.h"
 
 #include <emscripten/emscripten.h>
 
@@ -9,10 +9,10 @@
 #endif
 
   // JS methods accessible from C++
-  extern OrthancStone::WidgetViewport* CreateWasmViewportFromCpp(const char* htmlCanvasId);
+  extern Deprecated::WidgetViewport* CreateWasmViewportFromCpp(const char* htmlCanvasId);
 
 #ifdef __cplusplus
 }
 #endif
 
-extern void AttachWidgetToWasmViewport(const char* htmlCanvasId, OrthancStone::IWidget* centralWidget);
+extern void AttachWidgetToWasmViewport(const char* htmlCanvasId, Deprecated::IWidget* centralWidget);
--- a/Platforms/Wasm/WasmWebService.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Platforms/Wasm/WasmWebService.cpp	Wed May 22 16:13:46 2019 +0200
@@ -6,9 +6,9 @@
 
 struct CachedSuccessNotification
 {
-  boost::shared_ptr<OrthancStone::BaseWebService::CachedHttpRequestSuccessMessage>    cachedMessage;
+  boost::shared_ptr<Deprecated::BaseWebService::CachedHttpRequestSuccessMessage>    cachedMessage;
   std::auto_ptr<Orthanc::IDynamicObject>                                              payload;
-  OrthancStone::MessageHandler<OrthancStone::IWebService::HttpRequestSuccessMessage>* successCallback;
+  OrthancStone::MessageHandler<Deprecated::IWebService::HttpRequestSuccessMessage>* successCallback;
 };
 
 
@@ -47,8 +47,8 @@
   {
     if (failureCallable != NULL)
     {
-      reinterpret_cast<OrthancStone::MessageHandler<OrthancStone::IWebService::HttpRequestErrorMessage>*>(failureCallable)->
-        Apply(OrthancStone::IWebService::HttpRequestErrorMessage(uri, reinterpret_cast<Orthanc::IDynamicObject*>(payload)));
+      reinterpret_cast<OrthancStone::MessageHandler<Deprecated::IWebService::HttpRequestErrorMessage>*>(failureCallable)->
+        Apply(Deprecated::IWebService::HttpRequestErrorMessage(uri, reinterpret_cast<Orthanc::IDynamicObject*>(payload)));
     }
   }
 
@@ -57,7 +57,7 @@
     // notification has been allocated in C++ and passed to JS.  It must be deleted by this method
     std::auto_ptr<CachedSuccessNotification> notification(reinterpret_cast<CachedSuccessNotification*>(notification_));
 
-    notification->successCallback->Apply(OrthancStone::IWebService::HttpRequestSuccessMessage(
+    notification->successCallback->Apply(Deprecated::IWebService::HttpRequestSuccessMessage(
       notification->cachedMessage->GetUri(), 
       notification->cachedMessage->GetAnswer(),
       notification->cachedMessage->GetAnswerSize(),
@@ -75,13 +75,13 @@
   {
     if (successCallable != NULL)
     {
-      OrthancStone::IWebService::HttpHeaders headers;
+      Deprecated::IWebService::HttpHeaders headers;
 
       // TODO - Parse "answerHeaders"
       //printf("TODO: parse headers [%s]\n", answerHeaders);
       
-      reinterpret_cast<OrthancStone::MessageHandler<OrthancStone::IWebService::HttpRequestSuccessMessage>*>(successCallable)->
-        Apply(OrthancStone::IWebService::HttpRequestSuccessMessage(uri, body, bodySize, headers,
+      reinterpret_cast<OrthancStone::MessageHandler<Deprecated::IWebService::HttpRequestSuccessMessage>*>(successCallable)->
+        Apply(Deprecated::IWebService::HttpRequestSuccessMessage(uri, body, bodySize, headers,
                                                                    reinterpret_cast<Orthanc::IDynamicObject*>(payload)));
     }
   }
@@ -92,9 +92,9 @@
 
 
 
-namespace OrthancStone
+namespace Deprecated
 {
-  MessageBroker* WasmWebService::broker_ = NULL;
+  OrthancStone::MessageBroker* WasmWebService::broker_ = NULL;
 
   void ToJsonString(std::string& output, const IWebService::HttpHeaders& headers)
   {
@@ -116,8 +116,8 @@
                                  const HttpHeaders& headers,
                                  const std::string& body,
                                  Orthanc::IDynamicObject* payload,
-                                 MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallable,
-                                 MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallable,
+                                 OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallable,
+                                 OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallable,
                                  unsigned int timeoutInSeconds)
   {
     std::string headersInJsonString;
@@ -129,8 +129,8 @@
   void WasmWebService::DeleteAsync(const std::string& relativeUri,
                                    const HttpHeaders& headers,
                                    Orthanc::IDynamicObject* payload,
-                                   MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallable,
-                                   MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallable,
+                                   OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallable,
+                                   OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage>* failureCallable,
                                    unsigned int timeoutInSeconds)
   {
     std::string headersInJsonString;
@@ -142,8 +142,8 @@
   void WasmWebService::GetAsyncInternal(const std::string &relativeUri,
                                         const HttpHeaders &headers,
                                         Orthanc::IDynamicObject *payload,
-                                        MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallable,
-                                        MessageHandler<IWebService::HttpRequestErrorMessage> *failureCallable,
+                                        OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallable,
+                                        OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage> *failureCallable,
                                         unsigned int timeoutInSeconds)
   {
     std::string headersInJsonString;
@@ -153,8 +153,8 @@
   }
 
   void WasmWebService::NotifyHttpSuccessLater(boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedMessage,
-                                                Orthanc::IDynamicObject* payload, // takes ownership
-                                                MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback)
+                                              Orthanc::IDynamicObject* payload, // takes ownership
+                                              OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage>* successCallback)
   {
     CachedSuccessNotification* notification = new CachedSuccessNotification();  // allocated on the heap, it will be passed to JS and deleted when coming back to C++
     notification->cachedMessage = cachedMessage;
--- a/Platforms/Wasm/WasmWebService.h	Wed May 22 16:01:34 2019 +0200
+++ b/Platforms/Wasm/WasmWebService.h	Wed May 22 16:13:46 2019 +0200
@@ -1,17 +1,17 @@
 #pragma once
 
-#include <Framework/Toolbox/BaseWebService.h>
+#include "../../Framework/Deprecated/Toolbox/BaseWebService.h"
 #include <Core/OrthancException.h>
 
-namespace OrthancStone
+namespace Deprecated
 {
 class WasmWebService : public BaseWebService
 {
 private:
-  static MessageBroker *broker_;
+  static OrthancStone::MessageBroker *broker_;
 
   // Private constructor => Singleton design pattern
-  WasmWebService(MessageBroker &broker) : BaseWebService(broker)
+  WasmWebService(OrthancStone::MessageBroker &broker) : BaseWebService(broker)
   {
   }
 
@@ -27,7 +27,7 @@
     return instance;
   }
 
-  static void SetBroker(MessageBroker &broker)
+  static void SetBroker(OrthancStone::MessageBroker &broker)
   {
     broker_ = &broker;
   }
@@ -36,27 +36,27 @@
                          const HttpHeaders &headers,
                          const std::string &body,
                          Orthanc::IDynamicObject *payload,
-                         MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallable,
-                         MessageHandler<IWebService::HttpRequestErrorMessage> *failureCallable = NULL,
+                         OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallable,
+                         OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage> *failureCallable = NULL,
                          unsigned int timeoutInSeconds = 60);
 
   virtual void DeleteAsync(const std::string &uri,
                            const HttpHeaders &headers,
                            Orthanc::IDynamicObject *payload,
-                           MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallable,
-                           MessageHandler<IWebService::HttpRequestErrorMessage> *failureCallable = NULL,
+                           OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallable,
+                           OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage> *failureCallable = NULL,
                            unsigned int timeoutInSeconds = 60);
 
 protected:
   virtual void GetAsyncInternal(const std::string &uri,
                                 const HttpHeaders &headers,
                                 Orthanc::IDynamicObject *payload,
-                                MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallable,
-                                MessageHandler<IWebService::HttpRequestErrorMessage> *failureCallable = NULL,
+                                OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallable,
+                                OrthancStone::MessageHandler<IWebService::HttpRequestErrorMessage> *failureCallable = NULL,
                                 unsigned int timeoutInSeconds = 60);
 
   virtual void NotifyHttpSuccessLater(boost::shared_ptr<BaseWebService::CachedHttpRequestSuccessMessage> cachedHttpMessage,
                                       Orthanc::IDynamicObject *payload, // takes ownership
-                                      MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallback);
+                                      OrthancStone::MessageHandler<IWebService::HttpRequestSuccessMessage> *successCallback);
 };
-} // namespace OrthancStone
+} // namespace Deprecated
--- a/Resources/CMake/OrthancStoneConfiguration.cmake	Wed May 22 16:01:34 2019 +0200
+++ b/Resources/CMake/OrthancStoneConfiguration.cmake	Wed May 22 16:13:46 2019 +0200
@@ -32,7 +32,6 @@
 
 include(${ORTHANC_ROOT}/Resources/CMake/OrthancFrameworkConfiguration.cmake)
 include_directories(${ORTHANC_ROOT})
-include_directories(${ORTHANC_ROOT}/Core/Images) # hack for the numerous #include "../Enumerations.h" in Orthanc to work
 
 
 #####################################################################
@@ -110,7 +109,7 @@
   message("SDL is enabled")
   include(${CMAKE_CURRENT_LIST_DIR}/SdlConfiguration.cmake)
   add_definitions(
-    -DORTHANC_ENABLE_NATIVE=1
+    -DORTHANC_ENABLE_THREADS=1
     -DORTHANC_ENABLE_QT=0
     -DORTHANC_ENABLE_SDL=1
     )
@@ -118,7 +117,7 @@
   message("QT is enabled")
   include(${CMAKE_CURRENT_LIST_DIR}/QtConfiguration.cmake)
   add_definitions(
-    -DORTHANC_ENABLE_NATIVE=1
+    -DORTHANC_ENABLE_THREADS=1
     -DORTHANC_ENABLE_QT=1
     -DORTHANC_ENABLE_SDL=0
     )
@@ -128,7 +127,7 @@
   add_definitions(
     -DORTHANC_ENABLE_SDL=0
     -DORTHANC_ENABLE_QT=0
-    -DORTHANC_ENABLE_NATIVE=0
+    -DORTHANC_ENABLE_THREADS=0
     )
 endif()
 
@@ -305,6 +304,58 @@
     DEPENDS "${ORTHANC_STONE_ROOT}/Platforms/Wasm/default-library.js")
 endif()
 
+
+if (ENABLE_STONE_DEPRECATED)
+  list(APPEND ORTHANC_STONE_SOURCES
+    ${ORTHANC_ROOT}/Plugins/Samples/Common/DicomDatasetReader.cpp
+    ${ORTHANC_ROOT}/Plugins/Samples/Common/DicomPath.cpp
+    ${ORTHANC_ROOT}/Plugins/Samples/Common/FullOrthancDataset.cpp
+    ${ORTHANC_ROOT}/Plugins/Samples/Common/IOrthancConnection.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/CircleMeasureTracker.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/ColorFrameRenderer.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/DicomSeriesVolumeSlicer.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/DicomStructureSetSlicer.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/FrameRenderer.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/GrayscaleFrameRenderer.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/IVolumeSlicer.h
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/LineLayerRenderer.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/LineMeasureTracker.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/RenderStyle.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Layers/SliceOutlineRenderer.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/SmartLoader.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/BaseWebService.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/DicomFrameConverter.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/DownloadStack.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/IDelayedCallExecutor.h
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/IWebService.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/OrthancApiClient.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/OrthancSlicesLoader.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/Slice.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Toolbox/ViewportGeometry.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Viewport/IMouseTracker.h
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Viewport/IStatusBar.h
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Viewport/IViewport.h
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Viewport/WidgetViewport.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Volumes/StructureSetLoader.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/CairoWidget.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/EmptyWidget.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/IWidget.h
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/IWorldSceneInteractor.h
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/IWorldSceneMouseTracker.h
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/LayoutWidget.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/PanMouseTracker.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/PanZoomMouseTracker.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/SliceViewerWidget.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/TestCairoWidget.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/TestWorldSceneWidget.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/WidgetBase.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/WorldSceneWidget.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/Widgets/ZoomMouseTracker.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Deprecated/dev.h
+    )
+endif()
+
+
 list(APPEND ORTHANC_STONE_SOURCES
   #${ORTHANC_STONE_ROOT}/Framework/Layers/SeriesFrameRendererFactory.cpp
   #${ORTHANC_STONE_ROOT}/Framework/Layers/SingleFrameRendererFactory.cpp
@@ -362,7 +413,7 @@
   ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/OneGesturePointerTracker.h
   ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/PointerTypes.h
   ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/ViewportController.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/ViewportController.h
+  ${ORTHANC_STONE_ROOT}/Framework/Scene2DViewport/ViewportController.h 
 
   ${ORTHANC_STONE_ROOT}/Framework/Fonts/FontRenderer.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Fonts/Glyph.cpp
@@ -370,19 +421,20 @@
   ${ORTHANC_STONE_ROOT}/Framework/Fonts/GlyphBitmapAlphabet.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Fonts/GlyphTextureAlphabet.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Fonts/TextBoundingBox.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Layers/CircleMeasureTracker.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Layers/ColorFrameRenderer.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Layers/DicomSeriesVolumeSlicer.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Layers/DicomStructureSetSlicer.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Layers/FrameRenderer.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Layers/GrayscaleFrameRenderer.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Layers/IVolumeSlicer.h
-  ${ORTHANC_STONE_ROOT}/Framework/Layers/LineLayerRenderer.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Layers/LineMeasureTracker.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Layers/RenderStyle.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Layers/SliceOutlineRenderer.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Loaders/BasicFetchingItemsSorter.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Loaders/BasicFetchingStrategy.cpp
+  ${ORTHANC_STONE_ROOT}/Framework/Messages/ICallable.h
+  ${ORTHANC_STONE_ROOT}/Framework/Messages/IMessage.h
+  ${ORTHANC_STONE_ROOT}/Framework/Messages/IObservable.cpp
+  ${ORTHANC_STONE_ROOT}/Framework/Messages/IObserver.h
+  ${ORTHANC_STONE_ROOT}/Framework/Messages/MessageBroker.h
+  ${ORTHANC_STONE_ROOT}/Framework/Messages/MessageForwarder.cpp
+  ${ORTHANC_STONE_ROOT}/Framework/Messages/Promise.h
+  ${ORTHANC_STONE_ROOT}/Framework/Oracle/GetOrthancImageCommand.cpp
+  ${ORTHANC_STONE_ROOT}/Framework/Oracle/GetOrthancWebViewerJpegCommand.cpp
+  ${ORTHANC_STONE_ROOT}/Framework/Oracle/OracleCommandWithPayload.cpp
+  ${ORTHANC_STONE_ROOT}/Framework/Oracle/OrthancRestApiCommand.cpp
+  ${ORTHANC_STONE_ROOT}/Framework/Oracle/ThreadedOracle.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyAlphaLayer.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyDicomLayer.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyLayer.cpp
@@ -399,75 +451,32 @@
   ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyTextLayer.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyWidget.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyWindowingTracker.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/SmartLoader.cpp
   ${ORTHANC_STONE_ROOT}/Framework/StoneEnumerations.cpp
   ${ORTHANC_STONE_ROOT}/Framework/StoneException.h
   ${ORTHANC_STONE_ROOT}/Framework/StoneInitialization.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Toolbox/AffineTransform2D.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Toolbox/BaseWebService.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Toolbox/CoordinateSystem3D.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Toolbox/DicomFrameConverter.cpp
+  ${ORTHANC_STONE_ROOT}/Framework/Toolbox/DicomInstanceParameters.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Toolbox/DicomStructureSet.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Toolbox/DownloadStack.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Toolbox/DynamicBitmap.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Toolbox/Extent2D.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Toolbox/FiniteProjectiveCamera.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Toolbox/GeometryToolbox.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Toolbox/IDelayedCallExecutor.h
-  ${ORTHANC_STONE_ROOT}/Framework/Toolbox/IWebService.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Toolbox/ImageGeometry.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Toolbox/LinearAlgebra.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Toolbox/MessagingToolbox.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Toolbox/OrientedBoundingBox.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Toolbox/OrthancApiClient.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Toolbox/OrthancSlicesLoader.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Toolbox/ParallelSlices.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Toolbox/ParallelSlicesCursor.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Toolbox/ShearWarpProjectiveTransform.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Toolbox/Slice.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Toolbox/SlicesSorter.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Toolbox/UndoRedoStack.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Toolbox/ViewportGeometry.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Toolbox/VolumeImageGeometry.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Viewport/CairoContext.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Viewport/CairoSurface.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Viewport/IMouseTracker.h
-  ${ORTHANC_STONE_ROOT}/Framework/Viewport/IStatusBar.h
-  ${ORTHANC_STONE_ROOT}/Framework/Viewport/IViewport.h
-  ${ORTHANC_STONE_ROOT}/Framework/Viewport/WidgetViewport.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Volumes/ImageBuffer3D.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Volumes/StructureSetLoader.cpp
+  ${ORTHANC_STONE_ROOT}/Framework/Volumes/OrientedVolumeBoundingBox.cpp
+  ${ORTHANC_STONE_ROOT}/Framework/Volumes/VolumeImageGeometry.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Volumes/VolumeReslicer.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Widgets/CairoWidget.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Widgets/EmptyWidget.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Widgets/IWidget.h
-  ${ORTHANC_STONE_ROOT}/Framework/Widgets/IWorldSceneInteractor.h
-  ${ORTHANC_STONE_ROOT}/Framework/Widgets/IWorldSceneMouseTracker.h
-  ${ORTHANC_STONE_ROOT}/Framework/Widgets/LayoutWidget.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Widgets/PanMouseTracker.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Widgets/PanZoomMouseTracker.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Widgets/SliceViewerWidget.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Widgets/TestCairoWidget.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Widgets/TestWorldSceneWidget.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Widgets/WidgetBase.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Widgets/WorldSceneWidget.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Widgets/ZoomMouseTracker.cpp
 
-  ${ORTHANC_STONE_ROOT}/Framework/dev.h
-
-  ${ORTHANC_STONE_ROOT}/Framework/Messages/ICallable.h
-  ${ORTHANC_STONE_ROOT}/Framework/Messages/IMessage.h
-  ${ORTHANC_STONE_ROOT}/Framework/Messages/IObservable.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Messages/IObserver.h
-  ${ORTHANC_STONE_ROOT}/Framework/Messages/MessageBroker.h
-  ${ORTHANC_STONE_ROOT}/Framework/Messages/MessageForwarder.cpp
-  ${ORTHANC_STONE_ROOT}/Framework/Messages/Promise.h
-
-  ${ORTHANC_ROOT}/Plugins/Samples/Common/DicomDatasetReader.cpp
-  ${ORTHANC_ROOT}/Plugins/Samples/Common/DicomPath.cpp
-  ${ORTHANC_ROOT}/Plugins/Samples/Common/FullOrthancDataset.cpp
-  ${ORTHANC_ROOT}/Plugins/Samples/Common/IOrthancConnection.cpp
-  
   ${PLATFORM_SOURCES}
   ${APPLICATIONS_SOURCES}
   ${ORTHANC_CORE_SOURCES}
@@ -515,8 +524,6 @@
 endif()
 
 
-include_directories(${ORTHANC_STONE_ROOT})
-
 
 ##
 ## TEST - Automatically add all ".h" headers to the list of sources
--- a/Resources/CMake/OrthancStoneParameters.cmake	Wed May 22 16:01:34 2019 +0200
+++ b/Resources/CMake/OrthancStoneParameters.cmake	Wed May 22 16:13:46 2019 +0200
@@ -54,3 +54,4 @@
 
 set(ENABLE_OPENGL ON CACHE INTERNAL "Enable support of OpenGL")
 set(ENABLE_WASM OFF CACHE INTERNAL "Enable support of WebAssembly")
+set(ENABLE_STONE_DEPRECATED OFF CACHE INTERNAL "Enable backward compatibility with deprecated Stone classes")
--- a/Resources/CodeGeneration/README.md	Wed May 22 16:01:34 2019 +0200
+++ b/Resources/CodeGeneration/README.md	Wed May 22 16:13:46 2019 +0200
@@ -10,11 +10,11 @@
 
 `testCppHandler` contains a C++ project that produces an executable 
 slurping a set of text files representing messages defined against 
-the `test_data/test1.yaml' schema and dumping them to `cout`.
+the `test_data/testTestStoneCodeGen.yaml' schema and dumping them to `cout`.
 
 'testWasmIntegrated` contains a small Web app demonstrating the 
 interaction between TypeScript and C++ in WASM.
 source ~/apps/emsdk/emsdk_env.sh
 
 
-Install Python and the following packages `pip install pyyaml jinja2`
+Install Python and the following packages `pip install pyyaml yamlloader jinja2`
--- a/Resources/CodeGeneration/stonegentool.py	Wed May 22 16:01:34 2019 +0200
+++ b/Resources/CodeGeneration/stonegentool.py	Wed May 22 16:13:46 2019 +0200
@@ -7,6 +7,7 @@
 from io import StringIO
 import time
 import datetime
+import yamlloader
 
 """
          1         2         3         4         5         6         7
@@ -186,7 +187,6 @@
   RegisterTemplateFunction(template,NeedsCppConstruction)
   RegisterTemplateFunction(template, DefaultValueToTs)
   RegisterTemplateFunction(template, DefaultValueToCpp)
-  RegisterTemplateFunction(template, sorted)
   return template
 
 def MakeTemplateFromFile(templateFileName):
@@ -533,7 +533,7 @@
         if not (nextCh == ' ' or nextCh == '\n'):
           lineNumber = schemaText.count("\n",0,i) + 1
           raise RuntimeError("Error at line " + str(lineNumber) + " in the schema: colons must be followed by a space or a newline!")
-    schema = yaml.load(schemaText, Loader = yaml.SafeLoader)
+    schema = yaml.load(schemaText, Loader = yamlloader.ordereddict.SafeLoader)
     return schema
 
 def GetTemplatingDictFromSchemaFilename(fn):
--- a/Resources/CodeGeneration/stonegentool_test.py	Wed May 22 16:01:34 2019 +0200
+++ b/Resources/CodeGeneration/stonegentool_test.py	Wed May 22 16:13:46 2019 +0200
@@ -87,13 +87,13 @@
     self.assertEqual(b4,["int","vector<string>"])
     
   def test_ParseSchema(self):
-    fn = os.path.join(os.path.dirname(__file__), 'test_data', 'test1.yaml')
+    fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml')
     obj = LoadSchema(fn)
     # we're happy if it does not crash :)
     CheckSchemaSchema(obj)
 
   def test_ComputeRequiredDeclarationOrder(self):
-    fn = os.path.join(os.path.dirname(__file__), 'test_data', 'test1.yaml')
+    fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml')
     obj = LoadSchema(fn)
     genOrder: str = ComputeRequiredDeclarationOrder(obj)
     self.assertEqual(5,len(genOrder))
@@ -110,7 +110,7 @@
 
   def test_genEnums(self):
     self.maxDiff = None
-    fn = os.path.join(os.path.dirname(__file__), 'test_data', 'test1.yaml')
+    fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml')
     obj = LoadSchema(fn)
     genOrder: str = ComputeRequiredDeclarationOrder(obj)
     processedSchema = ProcessSchema(obj, genOrder)
@@ -127,13 +127,15 @@
     self.assertTrue('someBs' in structs['C']['fields'])
     self.assertTrue('CrispType' in enums)
     self.assertTrue('Message1' in structs)
-    self.assertEqual('int32', structs['Message1']['fields']['a'].type)
-    self.assertEqual('string', structs['Message1']['fields']['b'].type)
-    self.assertEqual('EnumMonth0', structs['Message1']['fields']['c'].type)
-    self.assertEqual('bool', structs['Message1']['fields']['d'].type)
+    self.assertEqual('int32', structs['Message1']['fields']['memberInt32'].type)
+    self.assertEqual('string', structs['Message1']['fields']['memberString'].type)
+    self.assertEqual('EnumMonth0', structs['Message1']['fields']['memberEnumMonth'].type)
+    self.assertEqual('bool', structs['Message1']['fields']['memberBool'].type)
+    self.assertEqual('float32', structs['Message1']['fields']['memberFloat32'].type)
+    self.assertEqual('float64', structs['Message1']['fields']['memberFloat64'].type)
 
   def test_GenerateTypeScriptEnums(self):
-    fn = os.path.join(os.path.dirname(__file__), 'test_data', 'test1.yaml')
+    fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml')
     tdico = GetTemplatingDictFromSchemaFilename(fn)
     template = Template("""  // end of generic methods
 {% for enum in enums%}  export enum {{enum['name']}} {
@@ -167,7 +169,7 @@
     self.assertEqual(renderedCodeRef,renderedCode)
 
   def test_GenerateCplusplusEnums(self):
-    fn = os.path.join(os.path.dirname(__file__), 'test_data', 'test1.yaml')
+    fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml')
     tdico = GetTemplatingDictFromSchemaFilename(fn)
     template = Template("""  // end of generic methods
 {% for enum in enums%}  enum {{enum['name']}} {
@@ -201,45 +203,9 @@
     self.assertEqual(renderedCodeRef,renderedCode)
 
   def test_generateTsStructType(self):
-    fn = os.path.join(os.path.dirname(__file__), 'test_data', 'test1.yaml')
+    fn = os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml')
     tdico = GetTemplatingDictFromSchemaFilename(fn)
-    ref = """  export class Message1 {
-    a: number;
-    b: string;
-    c: EnumMonth0;
-    d: boolean;
-    public StoneSerialize(): string {
-      let container: object = {};
-      container['type'] = 'VsolStuff.Message1';
-      container['value'] = this;
-      return JSON.stringify(container);
-    }
-  };
 
-  export class Message2 {
-    toto: string;
-    tata: Message1[];
-    tutu: string[];
-    titi: Map<string, string>;
-    lulu: Map<string, Message1>;
-
-    constructor()
-    {
-      this.tata = new Array<Message1>();
-      this.tutu = new Array<string>();
-      this.titi = new Map<string, string>();
-      this.lulu = new Map<string, Message1>();  
-    }
-
-    public StoneSerialize(): string {
-      let container: object = {};
-      container['type'] = 'VsolStuff.Message2';
-      container['value'] = this;
-      return JSON.stringify(container);
-    }
-  };
-
-"""
 #     template = MakeTemplate("""  // end of generic methods
 # {% for struct in struct%}  export class {{struct['name']}} {
 #   {% for key in struct['fields']%}    {{key}}:{{struct['fields'][key]}},
@@ -290,7 +256,7 @@
 
     public StoneSerialize(): string {
       let container: object = {};
-      container['type'] = 'VsolMessages.A';
+      container['type'] = 'TestStoneCodeGen.A';
       container['value'] = this;
       return JSON.stringify(container);
     }
@@ -307,7 +273,7 @@
 
     public StoneSerialize(): string {
       let container: object = {};
-      container['type'] = 'VsolMessages.B';
+      container['type'] = 'TestStoneCodeGen.B';
       container['value'] = this;
       return JSON.stringify(container);
     }
@@ -324,53 +290,63 @@
 
     public StoneSerialize(): string {
       let container: object = {};
-      container['type'] = 'VsolMessages.C';
+      container['type'] = 'TestStoneCodeGen.C';
       container['value'] = this;
       return JSON.stringify(container);
     }
   };
 
   export class Message1 {
-    a:number;
-    b:string;
-    c:EnumMonth0;
-    d:boolean;
+    memberInt32:number;
+    memberString:string;
+    memberEnumMonth:EnumMonth0;
+    memberBool:boolean;
+    memberFloat32:number;
+    memberFloat64:number;
 
     constructor() {
-      this.a = new number();
-      this.b = new string();
-      this.c = new EnumMonth0();
-      this.d = new boolean();
+      this.memberInt32 = new number();
+      this.memberString = new string();
+      this.memberEnumMonth = new EnumMonth0();
+      this.memberBool = new boolean();
+      this.memberFloat32 = new number();
+      this.memberFloat64 = new number();
     }
 
     public StoneSerialize(): string {
       let container: object = {};
-      container['type'] = 'VsolMessages.Message1';
+      container['type'] = 'TestStoneCodeGen.Message1';
       container['value'] = this;
       return JSON.stringify(container);
     }
   };
 
   export class Message2 {
-    toto:string;
-    tata:Array<Message1>;
-    tutu:Array<string>;
-    titi:Map<string, string>;
-    lulu:Map<string, Message1>;
-    movieType:MovieType;
+    memberString:string;
+    memberStringWithDefault:string;
+    memberVectorOfMessage1:Array<Message1>;
+    memberVectorOfString:Array<string>;
+    memberMapStringString:Map<string, string>;
+    memberMapStringStruct:Map<string, Message1>;
+    memberMapEnumFloat:Map<CrispType, number>;
+    memberEnumMovieType:MovieType;
+    memberJson:Object;
 
     constructor() {
-      this.toto = new string();
-      this.tata = new Array<Message1>();
-      this.tutu = new Array<string>();
-      this.titi = new Map<string, string>();
-      this.lulu = new Map<string, Message1>();
-      this.movieType = new MovieType();
+      this.memberString = new string();
+      this.memberStringWithDefault = new string();
+      this.memberVectorOfMessage1 = new Array<Message1>();
+      this.memberVectorOfString = new Array<string>();
+      this.memberMapStringString = new Map<string, string>();
+      this.memberMapStringStruct = new Map<string, Message1>();
+      this.memberMapEnumFloat = new Map<CrispType, number>();
+      this.memberEnumMovieType = new MovieType();
+      this.memberJson = new Object();
     }
 
     public StoneSerialize(): string {
       let container: object = {};
-      container['type'] = 'VsolMessages.Message2';
+      container['type'] = 'TestStoneCodeGen.Message2';
       container['value'] = this;
       return JSON.stringify(container);
     }
@@ -383,7 +359,7 @@
 
   def test_generateWholeTsFile(self):
     schemaFile = \
-      os.path.join(os.path.dirname(__file__), 'test_data', 'test1.yaml')
+      os.path.join(os.path.dirname(__file__), 'test_data', 'testTestStoneCodeGen.yaml')
     tdico = GetTemplatingDictFromSchemaFilename(schemaFile)
     tsTemplateFile = \
       os.path.join(os.path.dirname(__file__), 'template.in.ts.j2')
--- a/Resources/CodeGeneration/template.in.h.j2	Wed May 22 16:01:34 2019 +0200
+++ b/Resources/CodeGeneration/template.in.h.j2	Wed May 22 16:13:46 2019 +0200
@@ -109,6 +109,21 @@
   }
 
   /** Throws in case of problem */
+  inline void _StoneDeserializeValue(float& destValue, const Json::Value& jsonValue)
+  {
+    if (!jsonValue.isNull())
+    {
+      destValue = jsonValue.asFloat();
+    }
+  }
+
+  inline Json::Value _StoneSerializeValue(float value)
+  {
+    Json::Value result(value);
+    return result;
+  }
+
+  /** Throws in case of problem */
   inline void _StoneDeserializeValue(bool& destValue, const Json::Value& jsonValue)
   {
     if (!jsonValue.isNull())
@@ -167,10 +182,21 @@
     return out;
   }
 
+  inline std::string ToString(const std::string& str)
+  {
+    return str;
+  }
+
+  inline void FromString(std::string& value, std::string strValue)
+  {
+    value = strValue;
+  }
+
+
   /** Throws in case of problem */
-  template<typename T>
+  template<typename TK, typename TV>
   void _StoneDeserializeValue(
-    std::map<std::string, T>& destValue, const Json::Value& jsonValue)
+    std::map<TK, TV>& destValue, const Json::Value& jsonValue)
   {
     if (!jsonValue.isNull())
     {
@@ -180,10 +206,12 @@
         itr != jsonValue.end();
         itr++)
       {
-        std::string key;
-        _StoneDeserializeValue(key, itr.key());
+        std::string strKey;
+        _StoneDeserializeValue(strKey, itr.key());
+        TK key;
+        FromString(key, strKey);  // if you have a compile error here, it means that your type is not suitable to be the key of a map (or you should overwrite the FromString/ToString in template.in.h.j2)
 
-        T innerDestValue;
+        TV innerDestValue;
         _StoneDeserializeValue(innerDestValue, *itr);
 
         destValue[key] = innerDestValue;
@@ -191,29 +219,30 @@
     }
   }
 
-  template<typename T>
-  Json::Value _StoneSerializeValue(const std::map<std::string,T>& value)
+  template<typename TK, typename TV>
+  Json::Value _StoneSerializeValue(const std::map<TK, TV>& value)
   {
     Json::Value result(Json::objectValue);
 
-    for (typename std::map<std::string, T>::const_iterator it = value.cbegin();
+    for (typename std::map<TK, TV>::const_iterator it = value.cbegin();
       it != value.cend(); ++it)
     {
       // it->first it->second
-      result[it->first] = _StoneSerializeValue(it->second);
+      result[ToString(it->first)] = _StoneSerializeValue(it->second);
     }
     return result;
   }
 
-  template<typename T>
-  std::ostream& StoneDumpValue(std::ostream& out, const std::map<std::string,T>& value, size_t indent)
+  template<typename TK, typename TV>
+  std::ostream& StoneDumpValue(std::ostream& out, const std::map<TK, TV>& value, size_t indent)
   {
     out << MakeIndent(indent) << "{\n";
-    for (typename std::map<std::string, T>::const_iterator it = value.cbegin();
+    for (typename std::map<TK, TV>::const_iterator it = value.cbegin();
       it != value.cend(); ++it)
     {
       out << MakeIndent(indent+2) << "\"" << it->first << "\" : ";
       StoneDumpValue(out, it->second, indent+2);
+      out << ", \n";
     }
     out << MakeIndent(indent) << "}\n";
     return out;
@@ -224,7 +253,7 @@
   void _StoneDeserializeValue(
     std::vector<T>& destValue, const Json::Value& jsonValue)
   {
-    if (!jsonValue.isNull())
+    if (!jsonValue.isNull() && jsonValue.isArray())
     {
       destValue.clear();
       destValue.reserve(jsonValue.size());
@@ -255,6 +284,7 @@
     for (size_t i = 0; i < value.size(); ++i)
     {
       StoneDumpValue(out, value[i], indent+2);
+      out << ", \n";
     }
     out << MakeIndent(indent) << "]\n";
     return out;
@@ -265,7 +295,7 @@
   void _StoneDeserializeValue(
     std::set<T>& destValue, const Json::Value& jsonValue)
   {
-    if (!jsonValue.isNull())
+    if (!jsonValue.isNull() && jsonValue.isArray())
     {
       destValue.clear();
       for (Json::Value::ArrayIndex i = 0; i != jsonValue.size(); i++)
@@ -295,6 +325,7 @@
     for (typename std::set<T>::const_iterator it = value.begin(); it != value.end(); ++it)
     {
       StoneDumpValue(out, *it, indent+2);
+      out << ", \n";
     }
     out << MakeIndent(indent) << "]\n";
     return out;
@@ -381,7 +412,7 @@
   {
 {% for key in enum['fields']%}    if( value == {{enum['name']}}_{{key}})
     {
-      out << MakeIndent(indent) << "{{key}}" << std::endl;
+      out << MakeIndent(indent) << "{{key}}";
     }
 {%endfor%}    return out;
   }
@@ -394,9 +425,9 @@
 
   struct {{struct['name']}}
   {
-{% if struct %}{% if struct['fields'] %}{% for key in sorted(struct['fields']) %}    {{CanonToCpp(struct['fields'][key]['type'])}} {{key}};
+{% if struct %}{% if struct['fields'] %}{% for key in struct['fields'] %}    {{CanonToCpp(struct['fields'][key]['type'])}} {{key}};
 {% endfor %}{% endif %}{% endif %}
-    {{struct['name']}}({% if struct %}{% if struct['fields'] %}{% for key in sorted(struct['fields']) %}{{CanonToCpp(struct['fields'][key]['type'])}} {{key}} = {% if struct['fields'][key]['defaultValue'] %}{{DefaultValueToCpp(rootName,enums,struct['fields'][key])}} {%else%} {{CanonToCpp(struct['fields'][key]['type'])}}() {%endif%} {{ ", " if not loop.last }}{% endfor %}{% endif %}{% endif %})
+    {{struct['name']}}({% if struct %}{% if struct['fields'] %}{% for key in struct['fields'] %}{{CanonToCpp(struct['fields'][key]['type'])}} {{key}} = {% if struct['fields'][key]['defaultValue'] %}{{DefaultValueToCpp(rootName,enums,struct['fields'][key])}} {%else%} {{CanonToCpp(struct['fields'][key]['type'])}}() {%endif%} {{ ", " if not loop.last }}{% endfor %}{% endif %}{% endif %})
     {
 {% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%}      this->{{key}} = {{key}};
 {% endfor %}{% endif %}{% endif %}    }
@@ -421,11 +452,11 @@
   inline std::ostream& StoneDumpValue(std::ostream& out, const {{struct['name']}}& value, size_t indent = 0)
   {
     out << MakeIndent(indent) << "{\n";
-{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%}    out << MakeIndent(indent) << "{{key}}:\n";
+{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%}    out << MakeIndent(indent+2) << "{{key}}: ";
     StoneDumpValue(out, value.{{key}},indent+2);
-    out << "\n";
+    out << ", \n";
 {% endfor %}{% endif %}{% endif %}
-    out << MakeIndent(indent) << "}\n";
+    out << MakeIndent(indent) << "}";
     return out;
   }
 
--- a/Resources/CodeGeneration/testCppHandler/CMakeLists.txt	Wed May 22 16:01:34 2019 +0200
+++ b/Resources/CodeGeneration/testCppHandler/CMakeLists.txt	Wed May 22 16:13:46 2019 +0200
@@ -3,13 +3,13 @@
 project(testCppHandler)
 
 set(testCppHandler_Codegen_Deps
-  ${CMAKE_CURRENT_LIST_DIR}/../test_data/test1.yaml 
+  ${CMAKE_CURRENT_LIST_DIR}/../test_data/testTestStoneCodeGen.yaml 
   ${CMAKE_CURRENT_LIST_DIR}/../template.in.h.j2
 ) 
 
 add_custom_command(
     OUTPUT  ${CMAKE_CURRENT_BINARY_DIR}/VsolMessages_generated.hpp
-    COMMAND python ${CMAKE_CURRENT_LIST_DIR}/../stonegentool.py -o ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_LIST_DIR}/../test_data/test1.yaml
+    COMMAND python ${CMAKE_CURRENT_LIST_DIR}/../stonegentool.py -o ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_LIST_DIR}/../test_data/testTestStoneCodeGen.yaml
     DEPENDS ${testCppHandler_Codegen_Deps}
 )
 
--- a/Resources/CodeGeneration/testCppHandler/README.md	Wed May 22 16:01:34 2019 +0200
+++ b/Resources/CodeGeneration/testCppHandler/README.md	Wed May 22 16:13:46 2019 +0200
@@ -25,8 +25,10 @@
 - `cmake -G "Visual Studio 15 2017 Win64" ..`  (modify for your current Visual Studio version)
 - `cmake --build . --config Debug` or - `cmake --build . --config Release`
 
+How to execute the test
+=======================
+- `cd test_data && testCppHandler --pattern=*.json`
 
 
 
 
-
--- a/Resources/CodeGeneration/testCppHandler/main.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Resources/CodeGeneration/testCppHandler/main.cpp	Wed May 22 16:13:46 2019 +0200
@@ -8,7 +8,7 @@
 #include <boost/program_options.hpp>
 using namespace boost::program_options;
 
-#include "VsolMessages_generated.hpp"
+#include "TestStoneCodeGen_generated.hpp"
 
 /**
 Transforms `str` by replacing occurrences of `oldStr` with `newStr`, using 
@@ -39,32 +39,32 @@
   return string(bytes.data(), fileSize);
 }
 
-class MyHandler : public VsolMessages::IHandler
+class MyHandler : public TestStoneCodeGen::IHandler
 {
 public:
-  virtual bool Handle(const VsolMessages::A& value) override
+  virtual bool Handle(const TestStoneCodeGen::A& value) override
   {
-    VsolMessages::StoneDumpValue(cout, value);
+    TestStoneCodeGen::StoneDumpValue(cout, value);
     return true;
   }
-  virtual bool Handle(const VsolMessages::B& value) override
+  virtual bool Handle(const TestStoneCodeGen::B& value) override
   {
-    VsolMessages::StoneDumpValue(cout, value);
+    TestStoneCodeGen::StoneDumpValue(cout, value);
     return true;
   }
-  virtual bool Handle(const VsolMessages::C& value) override
+  virtual bool Handle(const TestStoneCodeGen::C& value) override
   {
-    VsolMessages::StoneDumpValue(cout, value);
+    TestStoneCodeGen::StoneDumpValue(cout, value);
     return true;
   }
-  virtual bool Handle(const VsolMessages::Message1& value) override
+  virtual bool Handle(const TestStoneCodeGen::Message1& value) override
   {
-    VsolMessages::StoneDumpValue(cout, value);
+    TestStoneCodeGen::StoneDumpValue(cout, value);
     return true;
   }
-  virtual bool Handle(const VsolMessages::Message2& value) override
+  virtual bool Handle(const TestStoneCodeGen::Message2& value) override
   {
-    VsolMessages::StoneDumpValue(cout, value);
+    TestStoneCodeGen::StoneDumpValue(cout, value);
     return true;
   }
 };
@@ -77,7 +77,7 @@
   cout << "+--------------------------------------------+\n";
   MyHandler handler;
   auto contents = SlurpFile(filePath.path().string());
-  VsolMessages::StoneDispatchToHandler(contents, &handler);
+  TestStoneCodeGen::StoneDispatchToHandler(contents, &handler);
 }
 
 int main(int argc, char** argv)
--- a/Resources/CodeGeneration/testCppHandler/test_data/test_Message2.json	Wed May 22 16:01:34 2019 +0200
+++ b/Resources/CodeGeneration/testCppHandler/test_data/test_Message2.json	Wed May 22 16:13:46 2019 +0200
@@ -1,40 +1,47 @@
 {
-  "type": "VsolMessages.Message2",
+  "type": "TestStoneCodeGen.Message2",
   "value": {
-    "tata": [
+    "memberVectorOfMessage1": [
       {
-        "a": 42,
-        "b": "Benjamin",
-        "c": 0,
-        "d": false
+        "memberInt32": 42,
+        "memberString": "Benjamin",
+        "memberEnumMonth": "January",
+        "memberBool": false,
+        "memberFloat32": 0.1,
+        "memberFloat64": -0.2
       },
       {
-        "a": 43,
-        "b": "Sandrine",
-        "c": 2
+        "memberInt32": 43,
+        "memberString": "Sandrine",
+        "memberEnumMonth": "March"
       }
     ],
-    "tutu": [
+    "memberVectorOfString": [
       "Mercadet",
       "Poisson"
     ],
-    "titi": {
+    "memberMapStringString": {
       "44": "key 44",
       "45": "key 45"
     },
-    "lulu": {
+    "memberMapStringStruct": {
       "54": {
-        "a": 43,
-        "b": "Sandrine",
-        "c": 2
+        "memberInt32": 43,
+        "memberString": "Sandrine",
+        "memberEnumMonth": "March"
       },
       "55": {
-        "a": 42,
-        "b": "Benjamin",
-        "c": 0,
-        "d": false
+        "memberInt32": 42,
+        "memberString": "Benjamin",
+        "memberEnumMonth": "January",
+        "memberBool": false
       }
     },
-    "toto": "Prout zizi"
+    "memberString": "Prout zizi",
+    "memberMapEnumFloat" : {
+      "SaltAndPepper" : 0.1,
+      "CreamAndChives" : -0.2
+    },
+    "memberJson" : {"custom-key": "custom-value"}
   }
 }
\ No newline at end of file
--- a/Resources/CodeGeneration/testWasmIntegrated/CMakeLists.txt	Wed May 22 16:01:34 2019 +0200
+++ b/Resources/CodeGeneration/testWasmIntegrated/CMakeLists.txt	Wed May 22 16:13:46 2019 +0200
@@ -21,14 +21,14 @@
 set(jsoncppRootDir ${CMAKE_CURRENT_LIST_DIR}/jsoncpp-1.8.4)
 
 add_custom_command(
-    OUTPUT  ${CMAKE_CURRENT_BINARY_DIR}/testWasmIntegratedCpp_generated.hpp ${CMAKE_CURRENT_BINARY_DIR}/testWasmIntegratedCpp_generated.ts
-    COMMAND python3 ${CMAKE_CURRENT_LIST_DIR}/../stonegentool.py -o ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_LIST_DIR}/testWasmIntegratedCpp_api.yaml
-    DEPENDS ${testCppHandler_Codegen_Deps} ${CMAKE_CURRENT_LIST_DIR}/testWasmIntegratedCpp_api.yaml 
+    OUTPUT  ${CMAKE_CURRENT_BINARY_DIR}/TestStoneCodeGen_generated.hpp ${CMAKE_CURRENT_BINARY_DIR}/TestStoneCodeGen_generated.ts
+    COMMAND python3 ${CMAKE_CURRENT_LIST_DIR}/../stonegentool.py -o ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_LIST_DIR}/../test_data/testTestStoneCodeGen.yaml
+    DEPENDS ${testCppHandler_Codegen_Deps} ${CMAKE_CURRENT_LIST_DIR}/../test_data/testTestStoneCodeGen.yaml 
 )
 
 add_executable(testWasmIntegratedCpp
   main.cpp
-  ${CMAKE_CURRENT_BINARY_DIR}/testWasmIntegratedCpp_generated.hpp
+  ${CMAKE_CURRENT_BINARY_DIR}/TestStoneCodeGen_generated.hpp
   ${jsoncppRootDir}/jsoncpp.cpp
   ${testCppHandler_Codegen_Deps})
 
--- a/Resources/CodeGeneration/testWasmIntegrated/DefaultLibrary.js	Wed May 22 16:01:34 2019 +0200
+++ b/Resources/CodeGeneration/testWasmIntegrated/DefaultLibrary.js	Wed May 22 16:13:46 2019 +0200
@@ -9,7 +9,7 @@
   },
   SendMessageFromCppJS: function(statusUpdateMessage) {
     var statusUpdateMessage_ = UTF8ToString(statusUpdateMessage);
-    SendMessageFromCpp(statusUpdateMessage_);
+    window.SendMessageFromCpp(statusUpdateMessage_);
   }
 });
 
--- a/Resources/CodeGeneration/testWasmIntegrated/build-web.sh	Wed May 22 16:01:34 2019 +0200
+++ b/Resources/CodeGeneration/testWasmIntegrated/build-web.sh	Wed May 22 16:13:46 2019 +0200
@@ -4,7 +4,7 @@
 mkdir -p build-final
 
 # compile TS to JS
-tsc --module commonjs --sourceMap -t ES2015 --outDir "build-tsc/" build-wasm/testWasmIntegratedCpp_generated.ts testWasmIntegrated.ts 
+tsc --module commonjs --sourceMap -t ES2015 --outDir "build-tsc/" build-wasm/TestStoneCodeGen_generated.ts testWasmIntegrated.ts 
 
 # bundle JS files to final build dir 
 browserify "build-tsc/build-wasm/testWasmIntegratedCpp_generated.js" "build-tsc/testWasmIntegrated.js" -o "build-final/testWasmIntegratedApp.js"
@@ -22,8 +22,10 @@
 # copy WASM binary to output dir
 cp build-wasm/testWasmIntegratedCpp.wasm  build-final/
 
-echo "...Serving files at http://127.0.0.1:8080/"
-echo "Please open build_final/testWasmIntegrated.html"
+cp ../test_data/testTestStoneCodeGen.yaml build-final/
+cp ../testCppHandler/test_data/test_Message2.json build-final/cppHandler_test_Message2.json
+
+echo "...Serving files at http://127.0.0.1:8080/build-final/testWasmIntegrated.html"
 
 sudo python3 serve.py
 
--- a/Resources/CodeGeneration/testWasmIntegrated/main.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Resources/CodeGeneration/testWasmIntegrated/main.cpp	Wed May 22 16:13:46 2019 +0200
@@ -1,7 +1,7 @@
 #include <iostream>
 #include <sstream>
 #include <emscripten/emscripten.h>
-#include "testWasmIntegratedCpp_generated.hpp"
+#include "TestStoneCodeGen_generated.hpp"
 
 using std::stringstream;
 
@@ -55,35 +55,72 @@
 #define HANDLE_MESSAGE(Type,value) \
   stringstream ss; \
   ss << "Received an instance of:\n" #Type "\n. Here's the dump:\n"; \
-  testWasmIntegratedCpp::StoneDumpValue(ss, value, 0); \
+  TestStoneCodeGen::StoneDumpValue(ss, value, 0); \
   SendFreeTextFromCppJS(ss.str().c_str()); \
   return true;
 
-class MyHandler : public testWasmIntegratedCpp::IHandler
+#define ECHO_MESSAGE(Type,value) \
+  stringstream ss; \
+  ss << "Received an instance of:\n" #Type "\n. Here's the dump:\n"; \
+  TestStoneCodeGen::StoneDumpValue(ss, value, 0); \
+  SendFreeTextFromCppJS(ss.str().c_str()); \
+  std::string serializedInCpp = StoneSerialize(value); \
+  SendMessageFromCppJS(serializedInCpp.c_str()); \
+  return true;
+
+class MyHandler : public TestStoneCodeGen::IHandler
 {
   public:
-    virtual bool Handle(const testWasmIntegratedCpp::A& value) override
+    virtual bool Handle(const TestStoneCodeGen::A& value) override
     {
-      HANDLE_MESSAGE(testWasmIntegratedCpp::A,value)
+      HANDLE_MESSAGE(TestStoneCodeGen::A,value)
+    }
+    virtual bool Handle(const TestStoneCodeGen::B& value) override
+    {
+      HANDLE_MESSAGE(TestStoneCodeGen::B,value)
     }
-    virtual bool Handle(const testWasmIntegratedCpp::B& value) override
+
+    virtual bool Handle(const TestStoneCodeGen::Message1& value) override
     {
-      HANDLE_MESSAGE(testWasmIntegratedCpp::B,value)
+      HANDLE_MESSAGE(TestStoneCodeGen::Message1,value)
+    }
+
+    virtual bool Handle(const TestStoneCodeGen::Message2& value) override
+    {
+      HANDLE_MESSAGE(TestStoneCodeGen::Message2,value)
     }
 
-    virtual bool Handle(const testWasmIntegratedCpp::Message1& value) override
+    virtual bool Handle(const TestStoneCodeGen::C& value) override
     {
-      HANDLE_MESSAGE(testWasmIntegratedCpp::Message1,value)
+      HANDLE_MESSAGE(TestStoneCodeGen::C,value)
+    }
+};
+
+class MyEchoHandler : public TestStoneCodeGen::IHandler
+{
+  public:
+    virtual bool Handle(const TestStoneCodeGen::A& value) override
+    {
+      ECHO_MESSAGE(TestStoneCodeGen::A,value)
+    }
+    virtual bool Handle(const TestStoneCodeGen::B& value) override
+    {
+      ECHO_MESSAGE(TestStoneCodeGen::B,value)
     }
 
-    virtual bool Handle(const testWasmIntegratedCpp::Message2& value) override
+    virtual bool Handle(const TestStoneCodeGen::Message1& value) override
     {
-      HANDLE_MESSAGE(testWasmIntegratedCpp::Message2,value)
+      ECHO_MESSAGE(TestStoneCodeGen::Message1,value)
     }
 
-    virtual bool Handle(const testWasmIntegratedCpp::C& value) override
+    virtual bool Handle(const TestStoneCodeGen::Message2& value) override
     {
-      HANDLE_MESSAGE(testWasmIntegratedCpp::C,value)
+      ECHO_MESSAGE(TestStoneCodeGen::Message2,value)
+    }
+
+    virtual bool Handle(const TestStoneCodeGen::C& value) override
+    {
+      ECHO_MESSAGE(TestStoneCodeGen::C,value)
     }
 };
 
@@ -92,10 +129,29 @@
     MyHandler handler;
     try
     {
-      bool handled = testWasmIntegratedCpp::StoneDispatchToHandler(message,&handler);
+      bool handled = TestStoneCodeGen::StoneDispatchToHandler(message,&handler);
       if(!handled)
       {
-        SendFreeTextFromCppJS("This message is valid JSON, but was not recognized!");  
+        SendFreeTextFromCppJS("This message is valid JSON, but was not handled!");  
+      }
+    }
+    catch(std::exception& e)
+    {
+      stringstream ss;
+      ss << "Error while parsing message: " << e.what() << "\n";
+      SendFreeTextFromCppJS(ss.str().c_str());  
+    }
+}
+
+extern "C" void EMSCRIPTEN_KEEPALIVE SendMessageToCppForEcho(const char* message)
+{
+    MyEchoHandler echoHandler;
+    try
+    {
+      bool handled = TestStoneCodeGen::StoneDispatchToHandler(message,&echoHandler);
+      if(!handled)
+      {
+        SendFreeTextFromCppJS("This message is valid JSON, but was not handled by the echo handler!");  
       }
     }
     catch(std::exception& e)
--- a/Resources/CodeGeneration/testWasmIntegrated/styles.css	Wed May 22 16:01:34 2019 +0200
+++ b/Resources/CodeGeneration/testWasmIntegrated/styles.css	Wed May 22 16:13:46 2019 +0200
@@ -9,7 +9,7 @@
         "Test1 Test2 Test3 Test4 . . ." 
         ". . . . . . ." 
         "Test5 Test6 Test7 Test8 . . ."
-        ". . . . . . ." 
+        "TestTsCppTs . . . . . ." 
         ". . . . . . ." 
         ;
     height: 480px;
@@ -59,6 +59,8 @@
 
 .TestWasm-Test8 { grid-area: Test8; }
 
+.TestWasm-ts-cpp-ts { grid-area: TestTsCppTs; }
+
 .TestWasm-button {
     width:80px;
 }
--- a/Resources/CodeGeneration/testWasmIntegrated/testWasmIntegrated.html	Wed May 22 16:01:34 2019 +0200
+++ b/Resources/CodeGeneration/testWasmIntegrated/testWasmIntegrated.html	Wed May 22 16:13:46 2019 +0200
@@ -31,7 +31,7 @@
         </div>
         
         <div class="TestWasm-Test1">
-          <button class="TestWasm-button" tool-selector="Test 1">Test 1</button>
+          <button class="TestWasm-button" tool-selector="Test CppHandler message2">Test CppHandler message2</button>
         </div>
         <div class="TestWasm-Test2">
           <button class="TestWasm-button" tool-selector="Test 2">Test 2</button>
@@ -54,6 +54,9 @@
         <div class="TestWasm-Test8">
           <button class="TestWasm-button" tool-selector="Test 8">Test 8</button>
         </div>
+        <div class="TestWasm-ts-cpp-ts">
+          <button class="TestWasm-button" tool-selector="Test-ts-cpp-ts">Test ts-cpp-ts</button>
+        </div>
   
         <!-- <button class="TestWasm-button" class="TestWasm-Test1" tool-selector="Test 1">Test 1</button>
         <button class="TestWasm-button" class="TestWasm-Test2" tool-selector="Test 2">Test 2</button>
--- a/Resources/CodeGeneration/testWasmIntegrated/testWasmIntegrated.ts	Wed May 22 16:01:34 2019 +0200
+++ b/Resources/CodeGeneration/testWasmIntegrated/testWasmIntegrated.ts	Wed May 22 16:13:46 2019 +0200
@@ -1,6 +1,8 @@
 var SendMessageToCpp: Function = null;
 export var TestWasmIntegratedModule : any;
 
+import * as TestStoneCodeGen from './build-wasm/TestStoneCodeGen_generated'
+
 /*
 +--------------------------------------------------+
 |  install emscripten handlers                     |
@@ -48,121 +50,23 @@
 |  define stock messages                           |
 +--------------------------------------------------+
 */
-let schemaText: string = `rootName: testWasmIntegratedCpp
-
-struct B:
-  someAs: vector<A>
-  someInts: vector<int32>
-
-struct C:
-  someBs: vector<B>
-  ddd:    vector<string>
-  definition: vector<json>
-
-struct A:
-  someStrings: vector<string>
-  someInts2: vector<int32>
-  movies: vector<MovieType>
-
-struct Message1:
-  a: int32
-  b: string
-  c: EnumMonth0
-  d: bool
-
-struct Message2:
-  toto: string
-  tata: vector<Message1>
-  tutu: vector<string>
-  titi: map<string, string>
-  lulu: map<string, Message1>
-  movieType: MovieType
-  definition: json
-
-enum MovieType:
-  - RomCom
-  - Horror
-  - ScienceFiction
-  - Vegetables
-
-enum CrispType:
-  - SaltAndPepper
-  - CreamAndChives
-  - Paprika
-  - Barbecue
-
-enum EnumMonth0:
-  - January
-  - February
-  - March
-`;
+let schemaText: string = null;
+fetch("testTestStoneCodeGen.yaml").then(function(res) {return res.text();}).then(function(text) {schemaText = text;});
 
 let stockSerializedMessages = new Map<string,string>();
-stockSerializedMessages["Test 1"] = `{
-  "type" : "testWasmIntegratedCpp.Message2",
-  "value" : 
-  {
-    "lulu" : 
-    {
-      "54" : 
-      {
-        "a" : 43,
-        "b" : "Sandrine",
-        "c" : "March",
-        "d" : true
-      },
-      "55" : 
-      {
-        "a" : 42,
-        "b" : "Benjamin",
-        "c" : "January",
-        "d" : false
-      }
-    },
-    "tata" : 
-    [
-      {
-        "a" : 42,
-        "b" : "Benjamin",
-        "c" : "March",
-        "d" : false
-      },
-      {
-        "a" : 43,
-        "b" : "Sandrine",
-        "c" : "January",
-        "d" : false
-      }
-    ],
-    "titi" : 
-    {
-      "44" : "key 44",
-      "45" : "key 45"
-    },
-    "toto" : "Prout zizi",
-    "tutu" : 
-    [
-      "Mercadet",
-      "Poisson"
-    ],
-    "definition":
-    {
-      "val" : [ "berk", 42 ],
-      "zozo" :
-      {
-        "23": "zloutch",
-        "lalala": 42
-      }
-    }
-  }
-}`;
+stockSerializedMessages["Test CppHandler message2"] = null;
+fetch("cppHandler_test_Message2.json").then(function(res) {return res.text();}).then(function(text) {stockSerializedMessages["Test CppHandler message2"] = text;});
+
 stockSerializedMessages["Test 2"] = ` {
-  "type" : "testWasmIntegratedCpp.Message1",
+  "type" : "TestStoneCodeGen.Message1",
   "value" : {
-    "a" : -987,
-    "b" : "Salomé",
-    "c" : 2,
-    "d" : true
+    "memberInt32" : -987,
+    "memberString" : "Salomé",
+    "memberEnumMonth" : "March",
+    "memberBool" : true,
+    "memberFloat32" : 0.1,
+    "memberFloat64" : -0.2,
+    "extraMember" : "don't care"
   }
 }`;
 stockSerializedMessages["Test 3"] = "Test 3 stock message sdfsfsdfsdf";
@@ -205,11 +109,60 @@
 }
 (<any> window).SendFreeTextFromCpp = SendFreeTextFromCpp;
 
+var referenceMessages = Array<any>();
+
+function testTsCppTs() {
+  var r = new TestStoneCodeGen.Message2();
+  r.memberEnumMovieType = TestStoneCodeGen.MovieType.RomCom;
+  r.memberStringWithDefault = "overriden";
+  r.memberMapEnumFloat[TestStoneCodeGen.CrispType.CreamAndChives] = 0.5;
+  r.memberString = "reference-messsage2-test1";
+
+  referenceMessages[r.memberString] = r;
+  var strMsg2 = r.StoneSerialize();
+  let SendMessageToCppForEchoLocal = (<any> window).Module.cwrap('SendMessageToCppForEcho', 'string', ['string']);
+  SendMessageToCppForEchoLocal(strMsg2);
+}
+
+class MyEchoHandler implements TestStoneCodeGen.IHandler
+{
+  public HandleMessage2(value:  TestStoneCodeGen.Message2): boolean
+  {
+    if (value.memberString in referenceMessages) {
+      let r = referenceMessages[value.memberString];
+      let equals = (value.memberStringWithDefault == r.memberStringWithDefault);
+      if (TestStoneCodeGen.CrispType.CreamAndChives in r.memberMapEnumFloat) {
+        equals == equals && r.memberMapEnumFloat[TestStoneCodeGen.CrispType.CreamAndChives] == value.memberMapEnumFloat[TestStoneCodeGen.CrispType.CreamAndChives];
+      }
+      // TODO continue comparison
+
+      if (equals) {
+        console.log("objects are equals after round trip");
+        return true;
+      }
+    }
+    console.log("problem after round trip");
+    return true;
+  }
+}
+
+function SendMessageFromCpp(txt: string):string
+{
+  setCppOutputValue(getCppOutputValue() + "\n" + txt);
+  TestStoneCodeGen.StoneDispatchToHandler(txt, new MyEchoHandler());
+  return "";
+}
+(<any> window).SendMessageFromCpp = SendMessageFromCpp;
+
+
 
 function ButtonClick(buttonName: string) {
   if (buttonName.startsWith('Test ')) {
     setSerializedInputValue(stockSerializedMessages[buttonName]);
   }
+  else if (buttonName == "Test-ts-cpp-ts") {
+    testTsCppTs();
+  }
   else if(buttonName == 'Trigger')
   {
     let serializedInputValue:string = getSerializedInputValue();
--- a/Resources/CodeGeneration/testWasmIntegrated/testWasmIntegratedCpp_api.yaml	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-#
-#        1         2         3         4         5         6         7         8
-# 345678901234567890123456789012345678901234567890123456789012345678901234567890
-#
-rootName: testWasmIntegratedCpp
-
-struct B:
-  someAs: vector<A>
-  someInts: vector<int32>
-
-struct C:
-  someBs: vector<B>
-  ddd:    vector<string>
-  definition: vector<json>
-
-struct A:
-  someStrings: vector<string>
-  someInts2: vector<int32>
-  movies: vector<MovieType>
-
-struct Message1:
-  a: int32
-  b: string
-  c: EnumMonth0
-  d: bool
-
-struct Message2:
-  toto: string
-  tata: vector<Message1>
-  tutu: vector<string>
-  titi: map<string, string>
-  lulu: map<string, Message1>
-  movieType: MovieType
-  definition: json
-
-enum MovieType:
-  - RomCom
-  - Horror
-  - ScienceFiction
-  - Vegetables
-
-enum CrispType:
-  - SaltAndPepper
-  - CreamAndChives
-  - Barbecue
-  - Paprika
-
-enum EnumMonth0:
-  - January
-  - February
-  - March
-
-enum Tata:
-  - Lolo
-  - Rrrrrrrrrrrr
-
-
-
--- a/Resources/CodeGeneration/test_data/test1.jsonc	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,87 +0,0 @@
-/*
-         1         2         3         4         5         6         7
-12345678901234567890123456789012345678901234567890123456789012345678901234567890
-*/
-{
-  "root_name":"test1",
-  "types": [
-    {
-      "name":"B",
-      "kind":"struct",
-      "fields": [
-        {
-          "name":"someAs",
-          "type":"vector<A>"
-        },
-        {
-          "name":"someInts",
-          "type":"vector<int32>"
-        }
-      ]
-    },
-    {
-      "name":"C",
-      "kind":"struct",
-      "fields": [
-        {
-          "name":"someBs",
-          "type":"vector<B>"
-        },
-        {
-          "name":"ddd",
-          "type":"vector<D>"
-        }
-      ]
-    },
-    {
-      "name":"A",
-      "kind":"struct",
-      "fields": [
-        {
-          "name":"someStrings",
-          "type":"vector<string>"
-        },
-        {
-          "name":"someInts2",
-          "type":"vector<int32>"
-        }
-      ]
-    },
-    {
-      "name":"MovieType",
-      "kind":"enum",
-      "fields": [
-        {
-          "name":"Romcom"
-        },
-        {
-          "name":"Horror"
-        },
-        {
-          "name":"ScienceFiction"
-        },
-        {
-          "name":"Vegetables"
-        }
-      ]
-    },
-    {
-      "name":"CrispType",
-      "kind":"enum",
-      "fields": [
-        {
-          "name":"SaltAndPepper"
-        },
-        {
-          "name":"CreamAndChives"
-        },
-        {
-          "name":"Paprika"
-        },
-        {
-          "name":"Barbecue"
-        }
-      ]
-    }
-  ]
-}
--- a/Resources/CodeGeneration/test_data/test1.yaml	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-#
-#        1         2         3         4         5         6         7         8
-# 345678901234567890123456789012345678901234567890123456789012345678901234567890
-#
-rootName: VsolMessages
-
-struct B:
-  someAs: vector<A>
-  someInts: vector<int32>
-
-struct C:
-  someBs: vector<B>
-  ddd:    vector<string>
-
-struct A:
-  someStrings: vector<string>
-  someInts2: vector<int32>
-  movies: vector<MovieType>
-
-struct Message1:
-  a: int32
-  b: string
-  c: EnumMonth0
-  d: bool
-
-struct Message2:
-  toto: string = "my-default-value"
-  tata: vector<Message1>
-  tutu: vector<string>
-  titi: map<string, string>
-  lulu: map<string, Message1>
-  movieType: MovieType
-
-enum MovieType:
-  - RomCom
-  - Horror
-  - ScienceFiction
-  - Vegetables
-
-enum CrispType:
-  - SaltAndPepper
-  - CreamAndChives
-  - Paprika
-  - Barbecue
-
-enum EnumMonth0:
-  - January
-  - February
-  - March
--- a/Resources/CodeGeneration/test_data/test1_bogus_json.jsonc	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-{
-  "root_name":"test1",
-  "types": [
-    {
-      "name":"B",
-      "kind":"struct",
-      "fields": [
-        {
-          "name":"someAs",
-          "type":"vector<A>"
-        }},
-        {
-          "name":"someInts",
-          "type":"vector<int32>"
-        }
-      ]
-    },
-    {
-      "name":"A",
-      "kind":"struct",
-      "fields": [
-        {
-          "name":"someStrings",
-          "type":"vector<string>"
-        },
-        {
-          "name":"someInts2",
-          "type":"vector<int32>"
-        }
-      ]
-    },
-    {
-      "name":"MovieType",
-      "kind":"enum",
-      "fields": [
-        {
-          "name":"Romcom",
-        },
-        {
-          "name":"Horror",
-        },
-        {
-          "name":"ScienceFiction",
-        },
-        {
-          "name":"Vegetables",
-        }
-    }
-  ]
-}
-
-/*
-         1         2         3         4         5         6         7
-12345678901234567890123456789012345678901234567890123456789012345678901234567890
-*/
--- a/Resources/CodeGeneration/test_data/test1_bogus_schema.jsonc	Wed May 22 16:01:34 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +0,0 @@
-{
-  "root_name":"test1",
-  "types": [
-    {
-      "name":"B",
-      "kind":"struct",
-      "fields": [
-        {
-          "name":"someAs",
-          "type":"vector<A>"
-        },
-        {
-          "name":"someInts",
-          "type":"vector<int32>"
-        }
-      ]
-    },
-    {
-      "name":"A",
-      "kiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiind":"struct",
-      "fields": [
-        {
-          "name":"someStrings",
-          "type":"vector<string>"
-        },
-        {
-          "name":"someInts2",
-          "type":"vector<int32>"
-        }
-      ]
-    },
-    {
-      "name":"MovieType",
-      "kind":"enum",
-      "fields": [
-        {
-          "naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaame":"Romcom"
-        },
-        {
-          "name":"Horror"
-        },
-        {
-          "name":"ScienceFiction"
-        },
-        {
-          "name":"Vegetables"
-        }
-      ]
-    }
-  ]
-}
-
-/*
-         1         2         3         4         5         6         7
-12345678901234567890123456789012345678901234567890123456789012345678901234567890
-*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/CodeGeneration/test_data/testTestStoneCodeGen.yaml	Wed May 22 16:13:46 2019 +0200
@@ -0,0 +1,64 @@
+#
+#        1         2         3         4         5         6         7         8
+# 345678901234567890123456789012345678901234567890123456789012345678901234567890
+#
+rootName: TestStoneCodeGen
+
+struct B:
+  __handler: cpp
+
+  someAs: vector<A>
+  someInts: vector<int32>
+
+struct C:
+  __handler: cpp
+
+  someBs: vector<B>
+  ddd:    vector<string>
+
+struct A:
+  __handler: cpp
+
+  someStrings: vector<string>
+  someInts2: vector<int32>
+  movies: vector<MovieType>
+
+struct Message1:
+  __handler: cpp
+
+  memberInt32: int32
+  memberString: string
+  memberEnumMonth: EnumMonth0
+  memberBool: bool
+  memberFloat32: float32
+  memberFloat64: float64
+
+struct Message2:
+  __handler: [cpp, ts]
+
+  memberString: string
+  memberStringWithDefault: string = "my-default-value"
+  memberVectorOfMessage1: vector<Message1>
+  memberVectorOfString: vector<string>
+  memberMapStringString: map<string, string>
+  memberMapStringStruct: map<string, Message1>
+  memberMapEnumFloat: map<CrispType, float32>
+  memberEnumMovieType: MovieType
+  memberJson: json
+
+enum MovieType:
+  - RomCom
+  - Horror
+  - ScienceFiction
+  - Vegetables
+
+enum CrispType:
+  - SaltAndPepper
+  - CreamAndChives
+  - Paprika
+  - Barbecue
+
+enum EnumMonth0:
+  - January
+  - February
+  - March
--- a/Samples/Sdl/Loader.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Samples/Sdl/Loader.cpp	Wed May 22 16:13:46 2019 +0200
@@ -18,20 +18,27 @@
  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  **/
 
+
+#include "../../Framework/Toolbox/DicomInstanceParameters.h"
+#include "../../Framework/Oracle/ThreadedOracle.h"
+#include "../../Framework/Oracle/GetOrthancWebViewerJpegCommand.h"
+#include "../../Framework/Oracle/GetOrthancImageCommand.h"
+#include "../../Framework/Oracle/OrthancRestApiCommand.h"
+#include "../../Framework/Oracle/SleepOracleCommand.h"
+#include "../../Framework/Oracle/OracleCommandExceptionMessage.h"
+#include "../../Framework/Messages/IMessageEmitter.h"
+#include "../../Framework/Oracle/OracleCommandWithPayload.h"
+#include "../../Framework/Oracle/IOracle.h"
+
 // From Stone
 #include "../../Framework/Loaders/BasicFetchingItemsSorter.h"
 #include "../../Framework/Loaders/BasicFetchingStrategy.h"
-#include "../../Framework/Messages/ICallable.h"
-#include "../../Framework/Messages/IMessage.h"
-#include "../../Framework/Messages/IObservable.h"
-#include "../../Framework/Messages/MessageBroker.h"
-#include "../../Framework/Scene2D/ColorTextureSceneLayer.h"
-#include "../../Framework/Scene2D/FloatTextureSceneLayer.h"
 #include "../../Framework/Scene2D/Scene2D.h"
 #include "../../Framework/StoneInitialization.h"
 #include "../../Framework/Toolbox/GeometryToolbox.h"
 #include "../../Framework/Toolbox/SlicesSorter.h"
 #include "../../Framework/Volumes/ImageBuffer3D.h"
+#include "../../Framework/Volumes/VolumeImageGeometry.h"
 
 // From Orthanc framework
 #include <Core/Compression/GzipCompressor.h>
@@ -40,12 +47,7 @@
 #include <Core/DicomFormat/DicomImageInformation.h>
 #include <Core/HttpClient.h>
 #include <Core/IDynamicObject.h>
-#include <Core/Images/Image.h>
 #include <Core/Images/ImageProcessing.h>
-#include <Core/Images/JpegReader.h>
-#include <Core/Images/PamReader.h>
-#include <Core/Images/PngReader.h>
-#include <Core/Images/PngWriter.h>
 #include <Core/Logging.h>
 #include <Core/MultiThreading/SharedMessageQueue.h>
 #include <Core/OrthancException.h>
@@ -61,1225 +63,24 @@
 
 
 
-namespace Refactoring
+namespace OrthancStone
 {
-  class IOracleCommand : public boost::noncopyable
-  {
-  public:
-    enum Type
-    {
-      Type_OrthancRestApi,
-      Type_GetOrthancImage,
-      Type_GetOrthancWebViewerJpeg
-    };
-
-    virtual ~IOracleCommand()
-    {
-    }
-
-    virtual Type GetType() const = 0;
-  };
-
-
-  class IMessageEmitter : public boost::noncopyable
-  {
-  public:
-    virtual ~IMessageEmitter()
-    {
-    }
-
-    virtual void EmitMessage(const OrthancStone::IObserver& observer,
-                             const OrthancStone::IMessage& message) = 0;
-  };
-
-
-  class IOracle : public boost::noncopyable
-  {
-  public:
-    virtual ~IOracle()
-    {
-    }
-
-    virtual void Schedule(const OrthancStone::IObserver& receiver,
-                          IOracleCommand* command) = 0;  // Takes ownership
-  };
-
-
-
-  class IVolumeSlicer : public boost::noncopyable
-  {
-  public:
-    virtual ~IVolumeSlicer()
-    {
-    }
-
-    virtual void SetViewportPlane(const OrthancStone::CoordinateSystem3D& plane) = 0;
-  };
-
-
-
-  class OracleCommandWithPayload : public IOracleCommand
-  {
-  private:
-    std::auto_ptr<Orthanc::IDynamicObject>  payload_;
-
-  public:
-    void SetPayload(Orthanc::IDynamicObject* payload)
-    {
-      if (payload == NULL)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-      }
-      else
-      {
-        payload_.reset(payload);
-      }    
-    }
-
-    bool HasPayload() const
-    {
-      return (payload_.get() != NULL);
-    }
-
-    const Orthanc::IDynamicObject& GetPayload() const
-    {
-      if (HasPayload())
-      {
-        return *payload_;
-      }
-      else
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-      }
-    }
-  };
-
-
-
-  class OracleCommandExceptionMessage : public OrthancStone::IMessage
-  {
-    ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
-
-  private:
-    const IOracleCommand&       command_;
-    Orthanc::OrthancException   exception_;
-
-  public:
-    OracleCommandExceptionMessage(const IOracleCommand& command,
-                                  const Orthanc::OrthancException& exception) :
-      command_(command),
-      exception_(exception)
-    {
-    }
-
-    OracleCommandExceptionMessage(const IOracleCommand& command,
-                                  const Orthanc::ErrorCode& error) :
-      command_(command),
-      exception_(error)
-    {
-    }
-
-    const IOracleCommand& GetCommand() const
-    {
-      return command_;
-    }
-    
-    const Orthanc::OrthancException& GetException() const
-    {
-      return exception_;
-    }
-  };
-  
-
-  typedef std::map<std::string, std::string>  HttpHeaders;
-
-  class OrthancRestApiCommand : public OracleCommandWithPayload
-  {
-  public:
-    class SuccessMessage : public OrthancStone::OriginMessage<OrthancRestApiCommand>
-    {
-      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
-      
-    private:
-      HttpHeaders   headers_;
-      std::string   answer_;
-
-    public:
-      SuccessMessage(const OrthancRestApiCommand& command,
-                     const HttpHeaders& answerHeaders,
-                     std::string& answer  /* will be swapped to avoid a memcpy() */) :
-        OriginMessage(command),
-        headers_(answerHeaders),
-        answer_(answer)
-      {
-      }
-
-      const std::string& GetAnswer() const
-      {
-        return answer_;
-      }
-
-      void ParseJsonBody(Json::Value& target) const
-      {
-        Json::Reader reader;
-        if (!reader.parse(answer_, target))
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-        }
-      }
-
-      const HttpHeaders&  GetAnswerHeaders() const
-      {
-        return headers_;
-      }
-    };
-
-
-  private:
-    Orthanc::HttpMethod  method_;
-    std::string          uri_;
-    std::string          body_;
-    HttpHeaders          headers_;
-    unsigned int         timeout_;
-
-    std::auto_ptr< OrthancStone::MessageHandler<SuccessMessage> >  successCallback_;
-    std::auto_ptr< OrthancStone::MessageHandler<OracleCommandExceptionMessage> >  failureCallback_;
-
-  public:
-    OrthancRestApiCommand() :
-      method_(Orthanc::HttpMethod_Get),
-      uri_("/"),
-      timeout_(10)
-    {
-    }
-
-    virtual Type GetType() const
-    {
-      return Type_OrthancRestApi;
-    }
-
-    void SetMethod(Orthanc::HttpMethod method)
-    {
-      method_ = method;
-    }
-
-    void SetUri(const std::string& uri)
-    {
-      uri_ = uri;
-    }
-
-    void SetBody(const std::string& body)
-    {
-      body_ = body;
-    }
-
-    void SetBody(const Json::Value& json)
-    {
-      Json::FastWriter writer;
-      body_ = writer.write(json);
-    }
-
-    void SetHttpHeaders(const HttpHeaders& headers)
-    {
-      headers_ = headers;
-    }
-
-    void SetHttpHeader(const std::string& key,
-                       const std::string& value)
-    {
-      headers_[key] = value;
-    }
-
-    Orthanc::HttpMethod GetMethod() const
-    {
-      return method_;
-    }
-
-    const std::string& GetUri() const
-    {
-      return uri_;
-    }
-
-    const std::string& GetBody() const
-    {
-      if (method_ == Orthanc::HttpMethod_Post ||
-          method_ == Orthanc::HttpMethod_Put)
-      {
-        return body_;
-      }
-      else
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-      }
-    }
-
-    const HttpHeaders& GetHttpHeaders() const
-    {
-      return headers_;
-    }
-
-    void SetTimeout(unsigned int seconds)
-    {
-      timeout_ = seconds;
-    }
-
-    unsigned int GetTimeout() const
-    {
-      return timeout_;
-    }
-  };
-
-
-
-
-  class GetOrthancImageCommand : public OracleCommandWithPayload
-  {
-  public:
-    class SuccessMessage : public OrthancStone::OriginMessage<GetOrthancImageCommand>
-    {
-      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
-      
-    private:
-      std::auto_ptr<Orthanc::ImageAccessor>  image_;
-      Orthanc::MimeType                      mime_;
-
-    public:
-      SuccessMessage(const GetOrthancImageCommand& command,
-                     Orthanc::ImageAccessor* image,   // Takes ownership
-                     Orthanc::MimeType mime) :
-        OriginMessage(command),
-        image_(image),
-        mime_(mime)
-      {
-        if (image == NULL)
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-        }
-      }
-
-      const Orthanc::ImageAccessor& GetImage() const
-      {
-        return *image_;
-      }
-
-      Orthanc::MimeType GetMimeType() const
-      {
-        return mime_;
-      }
-    };
-
-
-  private:
-    std::string           uri_;
-    HttpHeaders           headers_;
-    unsigned int          timeout_;
-    bool                  hasExpectedFormat_;
-    Orthanc::PixelFormat  expectedFormat_;
-
-    std::auto_ptr< OrthancStone::MessageHandler<SuccessMessage> >  successCallback_;
-    std::auto_ptr< OrthancStone::MessageHandler<OracleCommandExceptionMessage> >  failureCallback_;
-
-  public:
-    GetOrthancImageCommand() :
-      uri_("/"),
-      timeout_(10),
-      hasExpectedFormat_(false)
-    {
-    }
-
-    virtual Type GetType() const
-    {
-      return Type_GetOrthancImage;
-    }
-
-    void SetExpectedPixelFormat(Orthanc::PixelFormat format)
-    {
-      hasExpectedFormat_ = true;
-      expectedFormat_ = format;
-    }
-
-    void SetUri(const std::string& uri)
-    {
-      uri_ = uri;
-    }
-
-    void SetInstanceUri(const std::string& instance,
-                        Orthanc::PixelFormat pixelFormat)
-    {
-      uri_ = "/instances/" + instance;
-          
-      switch (pixelFormat)
-      {
-        case Orthanc::PixelFormat_RGB24:
-          uri_ += "/preview";
-          break;
-      
-        case Orthanc::PixelFormat_Grayscale16:
-          uri_ += "/image-uint16";
-          break;
-      
-        case Orthanc::PixelFormat_SignedGrayscale16:
-          uri_ += "/image-int16";
-          break;
-      
-        default:
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-      }
-    }
-
-    void SetHttpHeader(const std::string& key,
-                       const std::string& value)
-    {
-      headers_[key] = value;
-    }
-
-    const std::string& GetUri() const
-    {
-      return uri_;
-    }
-
-    const HttpHeaders& GetHttpHeaders() const
-    {
-      return headers_;
-    }
-
-    void SetTimeout(unsigned int seconds)
-    {
-      timeout_ = seconds;
-    }
-
-    unsigned int GetTimeout() const
-    {
-      return timeout_;
-    }
-
-    void ProcessHttpAnswer(IMessageEmitter& emitter,
-                           const OrthancStone::IObserver& receiver,
-                           const std::string& answer,
-                           const HttpHeaders& answerHeaders) const
-    {
-      Orthanc::MimeType contentType = Orthanc::MimeType_Binary;
-
-      for (HttpHeaders::const_iterator it = answerHeaders.begin(); 
-           it != answerHeaders.end(); ++it)
-      {
-        std::string s;
-        Orthanc::Toolbox::ToLowerCase(s, it->first);
-
-        if (s == "content-type")
-        {
-          contentType = Orthanc::StringToMimeType(it->second);
-          break;
-        }
-      }
-
-      std::auto_ptr<Orthanc::ImageAccessor> image;
-
-      switch (contentType)
-      {
-        case Orthanc::MimeType_Png:
-        {
-          image.reset(new Orthanc::PngReader);
-          dynamic_cast<Orthanc::PngReader&>(*image).ReadFromMemory(answer);
-          break;
-        }
-
-        case Orthanc::MimeType_Pam:
-        {
-          image.reset(new Orthanc::PamReader);
-          dynamic_cast<Orthanc::PamReader&>(*image).ReadFromMemory(answer);
-          break;
-        }
-
-        case Orthanc::MimeType_Jpeg:
-        {
-          image.reset(new Orthanc::JpegReader);
-          dynamic_cast<Orthanc::JpegReader&>(*image).ReadFromMemory(answer);
-          break;
-        }
-
-        default:
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol,
-                                          "Unsupported HTTP Content-Type for an image: " + 
-                                          std::string(Orthanc::EnumerationToString(contentType)));
-      }
-
-      if (hasExpectedFormat_)
-      {
-        if (expectedFormat_ == Orthanc::PixelFormat_SignedGrayscale16 &&
-            image->GetFormat() == Orthanc::PixelFormat_Grayscale16)
-        {
-          image->SetFormat(Orthanc::PixelFormat_SignedGrayscale16);
-        }
-
-        if (expectedFormat_ != image->GetFormat())
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
-        }
-      }
-
-      SuccessMessage message(*this, image.release(), contentType);
-      emitter.EmitMessage(receiver, message);
-    }
-  };
-
-
-
-  class GetOrthancWebViewerJpegCommand : public OracleCommandWithPayload
-  {
-  public:
-    class SuccessMessage : public OrthancStone::OriginMessage<GetOrthancWebViewerJpegCommand>
-    {
-      ORTHANC_STONE_MESSAGE(__FILE__, __LINE__);
-      
-    private:
-      std::auto_ptr<Orthanc::ImageAccessor>  image_;
-
-    public:
-      SuccessMessage(const GetOrthancWebViewerJpegCommand& command,
-                     Orthanc::ImageAccessor* image) :   // Takes ownership
-        OriginMessage(command),
-        image_(image)
-      {
-        if (image == NULL)
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-        }
-      }
-
-      const Orthanc::ImageAccessor& GetImage() const
-      {
-        return *image_;
-      }
-    };
-
-  private:
-    std::string           instanceId_;
-    unsigned int          frame_;
-    unsigned int          quality_;
-    HttpHeaders           headers_;
-    unsigned int          timeout_;
-    Orthanc::PixelFormat  expectedFormat_;
-
-    std::auto_ptr< OrthancStone::MessageHandler<SuccessMessage> >  successCallback_;
-    std::auto_ptr< OrthancStone::MessageHandler<OracleCommandExceptionMessage> >  failureCallback_;
-
-  public:
-    GetOrthancWebViewerJpegCommand() :
-      frame_(0),
-      quality_(95),
-      timeout_(10),
-      expectedFormat_(Orthanc::PixelFormat_Grayscale8)
-    {
-    }
-
-    virtual Type GetType() const
-    {
-      return Type_GetOrthancWebViewerJpeg;
-    }
-
-    void SetExpectedPixelFormat(Orthanc::PixelFormat format)
-    {
-      expectedFormat_ = format;
-    }
-
-    void SetInstance(const std::string& instanceId)
-    {
-      instanceId_ = instanceId;
-    }
-
-    void SetFrame(unsigned int frame)
-    {
-      frame_ = frame;
-    }
-
-    void SetQuality(unsigned int quality)
-    {
-      if (quality <= 0 ||
-          quality > 100)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-      }
-      else
-      {
-        quality_ = quality;
-      }
-    }
-
-    void SetHttpHeader(const std::string& key,
-                       const std::string& value)
-    {
-      headers_[key] = value;
-    }
-
-    Orthanc::PixelFormat GetExpectedPixelFormat() const
-    {
-      return expectedFormat_;
-    }
-
-    const std::string& GetInstanceId() const
-    {
-      return instanceId_;
-    }
-
-    unsigned int GetFrame() const
-    {
-      return frame_;
-    }
-
-    unsigned int GetQuality() const
-    {
-      return quality_;
-    }
-
-    const HttpHeaders& GetHttpHeaders() const
-    {
-      return headers_;
-    }
-
-    void SetTimeout(unsigned int seconds)
-    {
-      timeout_ = seconds;
-    }
-
-    unsigned int GetTimeout() const
-    {
-      return timeout_;
-    }
-
-    std::string GetUri() const
-    {
-      return ("/web-viewer/instances/jpeg" + boost::lexical_cast<std::string>(quality_) +
-              "-" + instanceId_ + "_" + boost::lexical_cast<std::string>(frame_));
-    }
-
-    void ProcessHttpAnswer(IMessageEmitter& emitter,
-                           const OrthancStone::IObserver& receiver,
-                           const std::string& answer) const
-    {
-      // This code comes from older "OrthancSlicesLoader::ParseSliceImageJpeg()"
-      
-      Json::Value encoded;
-
-      {
-        Json::Reader reader;
-        if (!reader.parse(answer, encoded))
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-        }
-      }
-
-      if (encoded.type() != Json::objectValue ||
-          !encoded.isMember("Orthanc") ||
-          encoded["Orthanc"].type() != Json::objectValue)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-      }
-    
-      const Json::Value& info = encoded["Orthanc"];
-      if (!info.isMember("PixelData") ||
-          !info.isMember("Stretched") ||
-          !info.isMember("Compression") ||
-          info["Compression"].type() != Json::stringValue ||
-          info["PixelData"].type() != Json::stringValue ||
-          info["Stretched"].type() != Json::booleanValue ||
-          info["Compression"].asString() != "Jpeg")
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-      }
-    
-      bool isSigned = false;
-      bool isStretched = info["Stretched"].asBool();
-    
-      if (info.isMember("IsSigned"))
-      {
-        if (info["IsSigned"].type() != Json::booleanValue)
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-        }
-        else
-        {
-          isSigned = info["IsSigned"].asBool();
-        }
-      }
-    
-      std::auto_ptr<Orthanc::ImageAccessor> reader;
-    
-      {
-        std::string jpeg;
-        Orthanc::Toolbox::DecodeBase64(jpeg, info["PixelData"].asString());
-      
-        reader.reset(new Orthanc::JpegReader);
-        dynamic_cast<Orthanc::JpegReader&>(*reader).ReadFromMemory(jpeg);
-      }
-    
-      if (reader->GetFormat() == Orthanc::PixelFormat_RGB24)  // This is a color image
-      {
-        if (expectedFormat_ != Orthanc::PixelFormat_RGB24)
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-        }
-      
-        if (isSigned || isStretched)
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-        }
-        else
-        {
-          SuccessMessage message(*this, reader.release());
-          emitter.EmitMessage(receiver, message);
-          return;
-        }
-      }
-    
-      if (reader->GetFormat() != Orthanc::PixelFormat_Grayscale8)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-      }
-    
-      if (!isStretched)
-      {
-        if (expectedFormat_ != reader->GetFormat())
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-        }
-        else
-        {
-          SuccessMessage message(*this, reader.release());
-          emitter.EmitMessage(receiver, message);
-          return;
-        }
-      }
-    
-      int32_t stretchLow = 0;
-      int32_t stretchHigh = 0;
-    
-      if (!info.isMember("StretchLow") ||
-          !info.isMember("StretchHigh") ||
-          info["StretchLow"].type() != Json::intValue ||
-          info["StretchHigh"].type() != Json::intValue)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-      }
-    
-      stretchLow = info["StretchLow"].asInt();
-      stretchHigh = info["StretchHigh"].asInt();
-    
-      if (stretchLow < -32768 ||
-          stretchHigh > 65535 ||
-          (stretchLow < 0 && stretchHigh > 32767))
-      {
-        // This range cannot be represented with a uint16_t or an int16_t
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-      }
-    
-      // Decode a grayscale JPEG 8bpp image coming from the Web viewer
-      std::auto_ptr<Orthanc::ImageAccessor> image
-        (new Orthanc::Image(expectedFormat_, reader->GetWidth(), reader->GetHeight(), false));
-
-      Orthanc::ImageProcessing::Convert(*image, *reader);
-      reader.reset();
-    
-      float scaling = static_cast<float>(stretchHigh - stretchLow) / 255.0f;
-    
-      if (!OrthancStone::LinearAlgebra::IsCloseToZero(scaling))
-      {
-        float offset = static_cast<float>(stretchLow) / scaling;
-        Orthanc::ImageProcessing::ShiftScale(*image, offset, scaling, true);
-      }
-    
-      SuccessMessage message(*this, image.release());
-      emitter.EmitMessage(receiver, message);
-    }
-  };
-
-
-
-
-
-  class DicomInstanceParameters :
-    public Orthanc::IDynamicObject  /* to be used as a payload of SlicesSorter */
-  {
-  private:
-    struct Data   // Struct to ease the copy constructor
-    {
-      std::string                       orthancInstanceId_;
-      std::string                       studyInstanceUid_;
-      std::string                       seriesInstanceUid_;
-      std::string                       sopInstanceUid_;
-      Orthanc::DicomImageInformation    imageInformation_;
-      OrthancStone::SopClassUid         sopClassUid_;
-      double                            thickness_;
-      double                            pixelSpacingX_;
-      double                            pixelSpacingY_;
-      OrthancStone::CoordinateSystem3D  geometry_;
-      OrthancStone::Vector              frameOffsets_;
-      bool                              isColor_;
-      bool                              hasRescale_;
-      double                            rescaleIntercept_;
-      double                            rescaleSlope_;
-      bool                              hasDefaultWindowing_;
-      float                             defaultWindowingCenter_;
-      float                             defaultWindowingWidth_;
-      Orthanc::PixelFormat              expectedPixelFormat_;
-
-      void ComputeDoseOffsets(const Orthanc::DicomMap& dicom)
-      {
-        // http://dicom.nema.org/medical/Dicom/2016a/output/chtml/part03/sect_C.8.8.3.2.html
-
-        {
-          std::string increment;
-
-          if (dicom.CopyToString(increment, Orthanc::DICOM_TAG_FRAME_INCREMENT_POINTER, false))
-          {
-            Orthanc::Toolbox::ToUpperCase(increment);
-            if (increment != "3004,000C")  // This is the "Grid Frame Offset Vector" tag
-            {
-              LOG(ERROR) << "RT-DOSE: Bad value for the \"FrameIncrementPointer\" tag";
-              return;
-            }
-          }
-        }
-
-        if (!OrthancStone::LinearAlgebra::ParseVector(frameOffsets_, dicom, Orthanc::DICOM_TAG_GRID_FRAME_OFFSET_VECTOR) ||
-            frameOffsets_.size() < imageInformation_.GetNumberOfFrames())
-        {
-          LOG(ERROR) << "RT-DOSE: No information about the 3D location of some slice(s)";
-          frameOffsets_.clear();
-        }
-        else
-        {
-          if (frameOffsets_.size() >= 2)
-          {
-            thickness_ = frameOffsets_[1] - frameOffsets_[0];
-
-            if (thickness_ < 0)
-            {
-              thickness_ = -thickness_;
-            }
-          }
-        }
-      }
-
-      Data(const Orthanc::DicomMap& dicom) :
-        imageInformation_(dicom)
-      {
-        if (imageInformation_.GetNumberOfFrames() <= 0)
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-        }
-
-        if (!dicom.CopyToString(studyInstanceUid_, Orthanc::DICOM_TAG_STUDY_INSTANCE_UID, false) ||
-            !dicom.CopyToString(seriesInstanceUid_, Orthanc::DICOM_TAG_SERIES_INSTANCE_UID, false) ||
-            !dicom.CopyToString(sopInstanceUid_, Orthanc::DICOM_TAG_SOP_INSTANCE_UID, false))
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-        }
-        
-        std::string s;
-        if (!dicom.CopyToString(s, Orthanc::DICOM_TAG_SOP_CLASS_UID, false))
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-        }
-        else
-        {
-          sopClassUid_ = OrthancStone::StringToSopClassUid(s);
-        }
-
-        if (!dicom.ParseDouble(thickness_, Orthanc::DICOM_TAG_SLICE_THICKNESS))
-        {
-          thickness_ = 100.0 * std::numeric_limits<double>::epsilon();
-        }
-
-        OrthancStone::GeometryToolbox::GetPixelSpacing(pixelSpacingX_, pixelSpacingY_, dicom);
-
-        std::string position, orientation;
-        if (dicom.CopyToString(position, Orthanc::DICOM_TAG_IMAGE_POSITION_PATIENT, false) &&
-            dicom.CopyToString(orientation, Orthanc::DICOM_TAG_IMAGE_ORIENTATION_PATIENT, false))
-        {
-          geometry_ = OrthancStone::CoordinateSystem3D(position, orientation);
-        }
-
-        if (sopClassUid_ == OrthancStone::SopClassUid_RTDose)
-        {
-          ComputeDoseOffsets(dicom);
-        }
-
-        isColor_ = (imageInformation_.GetPhotometricInterpretation() != Orthanc::PhotometricInterpretation_Monochrome1 &&
-                    imageInformation_.GetPhotometricInterpretation() != Orthanc::PhotometricInterpretation_Monochrome2);
-
-        double doseGridScaling;
-
-        if (dicom.ParseDouble(rescaleIntercept_, Orthanc::DICOM_TAG_RESCALE_INTERCEPT) &&
-            dicom.ParseDouble(rescaleSlope_, Orthanc::DICOM_TAG_RESCALE_SLOPE))
-        {
-          hasRescale_ = true;
-        }
-        else if (dicom.ParseDouble(doseGridScaling, Orthanc::DICOM_TAG_DOSE_GRID_SCALING))
-        {
-          hasRescale_ = true;
-          rescaleIntercept_ = 0;
-          rescaleSlope_ = doseGridScaling;
-        }
-        else
-        {
-          hasRescale_ = false;
-        }
-
-        OrthancStone::Vector c, w;
-        if (OrthancStone::LinearAlgebra::ParseVector(c, dicom, Orthanc::DICOM_TAG_WINDOW_CENTER) &&
-            OrthancStone::LinearAlgebra::ParseVector(w, dicom, Orthanc::DICOM_TAG_WINDOW_WIDTH) &&
-            c.size() > 0 && 
-            w.size() > 0)
-        {
-          hasDefaultWindowing_ = true;
-          defaultWindowingCenter_ = static_cast<float>(c[0]);
-          defaultWindowingWidth_ = static_cast<float>(w[0]);
-        }
-        else
-        {
-          hasDefaultWindowing_ = false;
-        }
-
-        if (sopClassUid_ == OrthancStone::SopClassUid_RTDose)
-        {
-          switch (imageInformation_.GetBitsStored())
-          {
-            case 16:
-              expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale16;
-              break;
-
-            case 32:
-              expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale32;
-              break;
-
-            default:
-              throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
-          } 
-        }
-        else if (isColor_)
-        {
-          expectedPixelFormat_ = Orthanc::PixelFormat_RGB24;
-        }
-        else if (imageInformation_.IsSigned())
-        {
-          expectedPixelFormat_ = Orthanc::PixelFormat_SignedGrayscale16;
-        }
-        else
-        {
-          expectedPixelFormat_ = Orthanc::PixelFormat_Grayscale16;
-        }
-      }
-
-      OrthancStone::CoordinateSystem3D  GetFrameGeometry(unsigned int frame) const
-      {
-        if (frame == 0)
-        {
-          return geometry_;
-        }
-        else if (frame >= imageInformation_.GetNumberOfFrames())
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-        }
-        else if (sopClassUid_ == OrthancStone::SopClassUid_RTDose)
-        {
-          if (frame >= frameOffsets_.size())
-          {
-            throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-          }
-
-          return OrthancStone::CoordinateSystem3D(
-            geometry_.GetOrigin() + frameOffsets_[frame] * geometry_.GetNormal(),
-            geometry_.GetAxisX(),
-            geometry_.GetAxisY());
-        }
-        else
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
-        }
-      }
-
-      // TODO - Is this necessary?
-      bool FrameContainsPlane(unsigned int frame,
-                              const OrthancStone::CoordinateSystem3D& plane) const
-      {
-        if (frame >= imageInformation_.GetNumberOfFrames())
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-        }
-
-        OrthancStone::CoordinateSystem3D tmp = geometry_;
-
-        if (frame != 0)
-        {
-          tmp = GetFrameGeometry(frame);
-        }
-
-        double distance;
-
-        return (OrthancStone::CoordinateSystem3D::GetDistance(distance, tmp, plane) &&
-                distance <= thickness_ / 2.0);
-      }
-
-      
-      void ApplyRescale(Orthanc::ImageAccessor& image,
-                        bool useDouble) const
-      {
-        if (image.GetFormat() != Orthanc::PixelFormat_Float32)
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
-        }
-    
-        if (hasRescale_)
-        {
-          const unsigned int width = image.GetWidth();
-          const unsigned int height = image.GetHeight();
-        
-          for (unsigned int y = 0; y < height; y++)
-          {
-            float* p = reinterpret_cast<float*>(image.GetRow(y));
-
-            if (useDouble)
-            {
-              // Slower, accurate implementation using double
-              for (unsigned int x = 0; x < width; x++, p++)
-              {
-                double value = static_cast<double>(*p);
-                *p = static_cast<float>(value * rescaleSlope_ + rescaleIntercept_);
-              }
-            }
-            else
-            {
-              // Fast, approximate implementation using float
-              for (unsigned int x = 0; x < width; x++, p++)
-              {
-                *p = (*p) * static_cast<float>(rescaleSlope_) + static_cast<float>(rescaleIntercept_);
-              }
-            }
-          }
-        }
-      }
-    };
-
-    
-    Data  data_;
-
-
-  public:
-    DicomInstanceParameters(const DicomInstanceParameters& other) :
-      data_(other.data_)
-    {
-    }
-
-    DicomInstanceParameters(const Orthanc::DicomMap& dicom) :
-      data_(dicom)
-    {
-    }
-
-    void SetOrthancInstanceIdentifier(const std::string& id)
-    {
-      data_.orthancInstanceId_ = id;
-    }
-
-    const std::string& GetOrthancInstanceIdentifier() const
-    {
-      return data_.orthancInstanceId_;
-    }
-
-    const Orthanc::DicomImageInformation& GetImageInformation() const
-    {
-      return data_.imageInformation_;
-    }
-
-    const std::string& GetStudyInstanceUid() const
-    {
-      return data_.studyInstanceUid_;
-    }
-
-    const std::string& GetSeriesInstanceUid() const
-    {
-      return data_.seriesInstanceUid_;
-    }
-
-    const std::string& GetSopInstanceUid() const
-    {
-      return data_.sopInstanceUid_;
-    }
-
-    OrthancStone::SopClassUid GetSopClassUid() const
-    {
-      return data_.sopClassUid_;
-    }
-
-    double GetThickness() const
-    {
-      return data_.thickness_;
-    }
-
-    double GetPixelSpacingX() const
-    {
-      return data_.pixelSpacingX_;
-    }
-
-    double GetPixelSpacingY() const
-    {
-      return data_.pixelSpacingY_;
-    }
-
-    const OrthancStone::CoordinateSystem3D&  GetGeometry() const
-    {
-      return data_.geometry_;
-    }
-
-    OrthancStone::CoordinateSystem3D  GetFrameGeometry(unsigned int frame) const
-    {
-      return data_.GetFrameGeometry(frame);
-    }
-
-    // TODO - Is this necessary?
-    bool FrameContainsPlane(unsigned int frame,
-                            const OrthancStone::CoordinateSystem3D& plane) const
-    {
-      return data_.FrameContainsPlane(frame, plane);
-    }
-
-    bool IsColor() const
-    {
-      return data_.isColor_;
-    }
-
-    bool HasRescale() const
-    {
-      return data_.hasRescale_;
-    }
-
-    double GetRescaleIntercept() const
-    {
-      if (data_.hasRescale_)
-      {
-        return data_.rescaleIntercept_;
-      }
-      else
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-      }
-    }
-
-    double GetRescaleSlope() const
-    {
-      if (data_.hasRescale_)
-      {
-        return data_.rescaleSlope_;
-      }
-      else
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-      }
-    }
-
-    bool HasDefaultWindowing() const
-    {
-      return data_.hasDefaultWindowing_;
-    }
-
-    float GetDefaultWindowingCenter() const
-    {
-      if (data_.hasDefaultWindowing_)
-      {
-        return data_.defaultWindowingCenter_;
-      }
-      else
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-      }
-    }
-
-    float GetDefaultWindowingWidth() const
-    {
-      if (data_.hasDefaultWindowing_)
-      {
-        return data_.defaultWindowingWidth_;
-      }
-      else
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-      }
-    }
-
-    Orthanc::PixelFormat GetExpectedPixelFormat() const
-    {
-      return data_.expectedPixelFormat_;
-    }
-
-
-    OrthancStone::TextureBaseSceneLayer* CreateTexture(const Orthanc::ImageAccessor& source) const
-    {
-      assert(sizeof(float) == 4);
-
-      Orthanc::PixelFormat sourceFormat = source.GetFormat();
-
-      if (sourceFormat != GetExpectedPixelFormat())
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat);
-      }
-
-      if (sourceFormat == Orthanc::PixelFormat_RGB24)
-      {
-        // This is the case of a color image. No conversion has to be done.
-        return new OrthancStone::ColorTextureSceneLayer(source);
-      }
-      else
-      {
-        if (sourceFormat != Orthanc::PixelFormat_Grayscale16 &&
-            sourceFormat != Orthanc::PixelFormat_Grayscale32 &&
-            sourceFormat != Orthanc::PixelFormat_SignedGrayscale16)
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
-        }
-
-        std::auto_ptr<OrthancStone::FloatTextureSceneLayer> texture;
-        
-        {
-          // This is the case of a grayscale frame. Convert it to Float32.
-          std::auto_ptr<Orthanc::Image> converted(new Orthanc::Image(Orthanc::PixelFormat_Float32, 
-                                                                     source.GetWidth(), 
-                                                                     source.GetHeight(),
-                                                                     false));
-          Orthanc::ImageProcessing::Convert(*converted, source);
-
-          // Correct rescale slope/intercept if need be
-          data_.ApplyRescale(*converted, (sourceFormat == Orthanc::PixelFormat_Grayscale32));
-
-          texture.reset(new OrthancStone::FloatTextureSceneLayer(*converted));
-        }
-
-        if (data_.hasDefaultWindowing_)
-        {
-          texture->SetCustomWindowing(data_.defaultWindowingCenter_,
-                                      data_.defaultWindowingWidth_);
-        }
-        
-        return texture.release();
-      }
-    }
-  };
-
-
   class DicomVolumeImage : public boost::noncopyable
   {
   private:
-    std::auto_ptr<OrthancStone::ImageBuffer3D>  image_;
-    std::vector<DicomInstanceParameters*>       slices_;
-    uint64_t                                    revision_;
-    std::vector<uint64_t>                       slicesRevision_;
-    std::vector<unsigned int>                   slicesQuality_;
+    std::auto_ptr<ImageBuffer3D>           image_;
+    std::auto_ptr<VolumeImageGeometry>     geometry_;
+    std::vector<DicomInstanceParameters*>  slices_;
+    uint64_t                               revision_;
+    std::vector<uint64_t>                  slicesRevision_;
+    std::vector<unsigned int>              slicesQuality_;
 
     void CheckSlice(size_t index,
                     const DicomInstanceParameters& reference) const
     {
       const DicomInstanceParameters& slice = *slices_[index];
       
-      if (!OrthancStone::GeometryToolbox::IsParallel(
+      if (!GeometryToolbox::IsParallel(
             reference.GetGeometry().GetNormal(),
             slice.GetGeometry().GetNormal()))
       {
@@ -1300,8 +101,8 @@
                                         "The width/height of slices are not constant in the volume image");
       }
 
-      if (!OrthancStone::LinearAlgebra::IsNear(reference.GetPixelSpacingX(), slice.GetPixelSpacingX()) ||
-          !OrthancStone::LinearAlgebra::IsNear(reference.GetPixelSpacingY(), slice.GetPixelSpacingY()))
+      if (!LinearAlgebra::IsNear(reference.GetPixelSpacingX(), slice.GetPixelSpacingX()) ||
+          !LinearAlgebra::IsNear(reference.GetPixelSpacingY(), slice.GetPixelSpacingY()))
       {
         throw Orthanc::OrthancException(Orthanc::ErrorCode_BadGeometry,
                                         "The pixel spacing of the slices change across the volume image");
@@ -1336,6 +137,7 @@
     void Clear()
     {
       image_.reset();
+      geometry_.reset();
       
       for (size_t i = 0; i < slices_.size(); i++)
       {
@@ -1376,7 +178,7 @@
     }
 
     // WARNING: The payload of "slices" must be of class "DicomInstanceParameters"
-    void SetGeometry(OrthancStone::SlicesSorter& slices)
+    void SetGeometry(SlicesSorter& slices)
     {
       Clear();
       
@@ -1386,11 +188,13 @@
                                         "Cannot sort the 3D slices of a DICOM series");          
       }
 
+      geometry_.reset(new VolumeImageGeometry);
+
       if (slices.GetSlicesCount() == 0)
       {
         // Empty volume
-        image_.reset(new OrthancStone::ImageBuffer3D(Orthanc::PixelFormat_Grayscale8, 0, 0, 0,
-                                                     false /* don't compute range */));
+        image_.reset(new ImageBuffer3D(Orthanc::PixelFormat_Grayscale8, 0, 0, 0,
+                                       false /* don't compute range */));
       }
       else
       {
@@ -1412,14 +216,15 @@
       
         const DicomInstanceParameters& parameters = *slices_[0];
 
-        image_.reset(new OrthancStone::ImageBuffer3D(parameters.GetExpectedPixelFormat(),
-                                                     parameters.GetImageInformation().GetWidth(),
-                                                     parameters.GetImageInformation().GetHeight(),
-                                                     slices.GetSlicesCount(), false /* don't compute range */));      
+        image_.reset(new ImageBuffer3D(parameters.GetExpectedPixelFormat(),
+                                       parameters.GetImageInformation().GetWidth(),
+                                       parameters.GetImageInformation().GetHeight(),
+                                       slices.GetSlicesCount(), false /* don't compute range */));      
 
-        image_->GetGeometry().SetAxialGeometry(slices.GetSliceGeometry(0));
-        image_->GetGeometry().SetVoxelDimensions(parameters.GetPixelSpacingX(),
-                                                 parameters.GetPixelSpacingY(), spacingZ);
+        geometry_->SetSize(image_->GetWidth(), image_->GetHeight(), image_->GetDepth());
+        geometry_->SetAxialGeometry(slices.GetSliceGeometry(0));
+        geometry_->SetVoxelDimensions(parameters.GetPixelSpacingX(),
+                                      parameters.GetPixelSpacingY(), spacingZ);
       }
       
       image_->Clear();
@@ -1434,10 +239,11 @@
 
     bool HasGeometry() const
     {
-      return (image_.get() != NULL);
+      return (image_.get() != NULL &&
+              geometry_.get() != NULL);
     }
 
-    const OrthancStone::ImageBuffer3D& GetImage() const
+    const ImageBuffer3D& GetImage() const
     {
       if (!HasGeometry())
       {
@@ -1449,6 +255,18 @@
       }
     }
 
+    const VolumeImageGeometry& GetGeometry() const
+    {
+      if (!HasGeometry())
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+      }
+      else
+      {
+        return *geometry_;
+      }
+    }
+
     size_t GetSlicesCount() const
     {
       if (!HasGeometry())
@@ -1483,8 +301,8 @@
       if (quality >= slicesQuality_[index])
       {
         {
-          OrthancStone::ImageBuffer3D::SliceWriter writer
-            (*image_, OrthancStone::VolumeProjection_Axial, index);
+          ImageBuffer3D::SliceWriter writer
+            (*image_, VolumeProjection_Axial, index);
           Orthanc::ImageProcessing::Copy(writer.GetAccessor(), image);
         }
         
@@ -1496,10 +314,10 @@
 
 
 
-  class IDicomVolumeSource : public boost::noncopyable
+  class IDicomVolumeImageSource : public boost::noncopyable
   {
   public:
-    virtual ~IDicomVolumeSource()
+    virtual ~IDicomVolumeImageSource()
     {
     }
 
@@ -1511,8 +329,8 @@
   
 
   class VolumeSeriesOrthancLoader :
-    public OrthancStone::IObserver,
-    public IDicomVolumeSource
+    public IObserver,
+    public IDicomVolumeImageSource
   {
   private:
     static const unsigned int LOW_QUALITY = 0;
@@ -1544,12 +362,11 @@
           throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
         }
 
-        std::auto_ptr<Refactoring::OracleCommandWithPayload> command;
+        std::auto_ptr<OracleCommandWithPayload> command;
         
         if (quality == BEST_QUALITY)
         {
-          std::auto_ptr<Refactoring::GetOrthancImageCommand> tmp(
-            new Refactoring::GetOrthancImageCommand);
+          std::auto_ptr<GetOrthancImageCommand> tmp(new GetOrthancImageCommand);
           tmp->SetHttpHeader("Accept-Encoding", "gzip");
           tmp->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Pam)));
           tmp->SetInstanceUri(instance, slice.GetExpectedPixelFormat());          
@@ -1558,8 +375,7 @@
         }
         else
         {
-          std::auto_ptr<Refactoring::GetOrthancWebViewerJpegCommand> tmp(
-            new Refactoring::GetOrthancWebViewerJpegCommand);
+          std::auto_ptr<GetOrthancWebViewerJpegCommand> tmp(new GetOrthancWebViewerJpegCommand);
           tmp->SetHttpHeader("Accept-Encoding", "gzip");
           tmp->SetInstance(instance);
           tmp->SetQuality((quality == 0 ? 50 : 90));
@@ -1586,7 +402,7 @@
       {
         Json::Value::Members instances = body.getMemberNames();
 
-        OrthancStone::SlicesSorter slices;
+        SlicesSorter slices;
         
         for (size_t i = 0; i < instances.size(); i++)
         {
@@ -1596,7 +412,7 @@
           std::auto_ptr<DicomInstanceParameters> instance(new DicomInstanceParameters(dicom));
           instance->SetOrthancInstanceIdentifier(instances[i]);
 
-          OrthancStone::CoordinateSystem3D geometry = instance->GetGeometry();
+          CoordinateSystem3D geometry = instance->GetGeometry();
           slices.AddSlice(geometry, instance.release());
         }
 
@@ -1605,8 +421,8 @@
 
       if (volume_.GetSlicesCount() != 0)
       {
-        strategy_.reset(new OrthancStone::BasicFetchingStrategy(
-                          new OrthancStone::BasicFetchingItemsSorter(volume_.GetSlicesCount()), BEST_QUALITY));
+        strategy_.reset(new BasicFetchingStrategy(
+                          new BasicFetchingItemsSorter(volume_.GetSlicesCount()), BEST_QUALITY));
 
         for (unsigned int i = 0; i < 4; i++)   // Schedule up to 4 simultaneous downloads (TODO - parameter)
         {
@@ -1616,7 +432,7 @@
     }
 
 
-    void LoadBestQualitySliceContent(const Refactoring::GetOrthancImageCommand::SuccessMessage& message)
+    void LoadBestQualitySliceContent(const GetOrthancImageCommand::SuccessMessage& message)
     {      
       volume_.SetSliceContent(GetSliceIndexPayload(message.GetOrigin()),
                               message.GetImage(), BEST_QUALITY);
@@ -1625,7 +441,7 @@
     }
 
 
-    void LoadJpegSliceContent(const Refactoring::GetOrthancWebViewerJpegCommand::SuccessMessage& message)
+    void LoadJpegSliceContent(const GetOrthancWebViewerJpegCommand::SuccessMessage& message)
     {
       unsigned int quality;
       
@@ -1653,25 +469,25 @@
     bool              active_;
     DicomVolumeImage  volume_;
     
-    std::auto_ptr<OrthancStone::IFetchingStrategy>   strategy_;
+    std::auto_ptr<IFetchingStrategy>   strategy_;
 
   public:
     VolumeSeriesOrthancLoader(IOracle& oracle,
-                              OrthancStone::IObservable& oracleObservable) :
+                              IObservable& oracleObservable) :
       IObserver(oracleObservable.GetBroker()),
       oracle_(oracle),
       active_(false)
     {
       oracleObservable.RegisterObserverCallback(
-        new OrthancStone::Callable<VolumeSeriesOrthancLoader, OrthancRestApiCommand::SuccessMessage>
+        new Callable<VolumeSeriesOrthancLoader, OrthancRestApiCommand::SuccessMessage>
         (*this, &VolumeSeriesOrthancLoader::LoadGeometry));
 
       oracleObservable.RegisterObserverCallback(
-        new OrthancStone::Callable<VolumeSeriesOrthancLoader, GetOrthancImageCommand::SuccessMessage>
+        new Callable<VolumeSeriesOrthancLoader, GetOrthancImageCommand::SuccessMessage>
         (*this, &VolumeSeriesOrthancLoader::LoadBestQualitySliceContent));
 
       oracleObservable.RegisterObserverCallback(
-        new OrthancStone::Callable<VolumeSeriesOrthancLoader, GetOrthancWebViewerJpegCommand::SuccessMessage>
+        new Callable<VolumeSeriesOrthancLoader, GetOrthancWebViewerJpegCommand::SuccessMessage>
         (*this, &VolumeSeriesOrthancLoader::LoadJpegSliceContent));
     }
 
@@ -1684,7 +500,7 @@
 
       active_ = true;
 
-      std::auto_ptr<Refactoring::OrthancRestApiCommand> command(new Refactoring::OrthancRestApiCommand);
+      std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand);
       command->SetUri("/series/" + seriesId + "/instances-tags");
 
       oracle_.Schedule(*this, command.release());
@@ -1728,7 +544,7 @@
 
     // TODO => Should be part of a second call if needed
 
-    std::auto_ptr<Refactoring::OrthancRestApiCommand> command(new Refactoring::OrthancRestApiCommand);
+    std::auto_ptr<OrthancRestApiCommand> command(new OrthancRestApiCommand);
     command->SetUri("/instances/" + instanceId + "/tags?ignore-length=3004-000c");
     command->SetPayload(new LoadInstanceGeometryHandler(*this));
 
@@ -1740,16 +556,16 @@
   /*  class VolumeSlicerBase : public IVolumeSlicer
       {
       private:
-      OrthancStone::Scene2D&            scene_;
+      Scene2D&            scene_;
       int                               layerDepth_;
       bool                              first_;
-      OrthancStone::CoordinateSystem3D  lastPlane_;
+      CoordinateSystem3D  lastPlane_;
 
       protected:
-      bool HasViewportPlaneChanged(const OrthancStone::CoordinateSystem3D& plane) const
+      bool HasViewportPlaneChanged(const CoordinateSystem3D& plane) const
       {
       if (first_ ||
-      !OrthancStone::LinearAlgebra::IsCloseToZero(
+      !LinearAlgebra::IsCloseToZero(
       boost::numeric::ublas::norm_2(lastPlane_.GetNormal() - plane.GetNormal())))
       {
       // This is the first rendering, or the plane has not the same orientation
@@ -1759,17 +575,17 @@
       {
       double offset1 = lastPlane_.ProjectAlongNormal(plane.GetOrigin());
       double offset2 = lastPlane_.ProjectAlongNormal(lastPlane_.GetOrigin());
-      return OrthancStone::LinearAlgebra::IsCloseToZero(offset2 - offset1);
+      return LinearAlgebra::IsCloseToZero(offset2 - offset1);
       }
       }
 
-      void SetLastViewportPlane(const OrthancStone::CoordinateSystem3D& plane)
+      void SetLastViewportPlane(const CoordinateSystem3D& plane)
       {
       first_ = false;
       lastPlane_ = plane;
       }
 
-      void SetLayer(OrthancStone::ISceneLayer* layer)
+      void SetLayer(ISceneLayer* layer)
       {
       scene_.SetLayer(layerDepth_, layer);
       }
@@ -1780,7 +596,7 @@
       }
     
       public:
-      VolumeSlicerBase(OrthancStone::Scene2D& scene,
+      VolumeSlicerBase(Scene2D& scene,
       int layerDepth) :
       scene_(scene),
       layerDepth_(layerDepth),
@@ -1791,22 +607,33 @@
   
 
 
+  class IVolumeSlicer : public boost::noncopyable
+  {
+  public:
+    virtual ~IVolumeSlicer()
+    {
+    }
+
+    virtual void SetViewportPlane(const CoordinateSystem3D& plane) = 0;
+  };
+
+
   class DicomVolumeMPRSlicer : public IVolumeSlicer
   {
   private:
-    bool                            linearInterpolation_;
-    OrthancStone::Scene2D&          scene_;
-    int                             layerDepth_;
-    IDicomVolumeSource&             source_;
-    bool                            first_;
-    OrthancStone::VolumeProjection  lastProjection_;
-    unsigned int                    lastSliceIndex_;
-    uint64_t                        lastSliceRevision_;
+    bool                      linearInterpolation_;
+    Scene2D&                  scene_;
+    int                       layerDepth_;
+    IDicomVolumeImageSource&  source_;
+    bool                      first_;
+    VolumeProjection          lastProjection_;
+    unsigned int              lastSliceIndex_;
+    uint64_t                  lastSliceRevision_;
 
   public:
-    DicomVolumeMPRSlicer(OrthancStone::Scene2D& scene,
+    DicomVolumeMPRSlicer(Scene2D& scene,
                          int layerDepth,
-                         IDicomVolumeSource& source) :
+                         IDicomVolumeImageSource& source) :
       linearInterpolation_(false),
       scene_(scene),
       layerDepth_(layerDepth),
@@ -1825,7 +652,7 @@
       return linearInterpolation_;
     }
     
-    virtual void SetViewportPlane(const OrthancStone::CoordinateSystem3D& plane)
+    virtual void SetViewportPlane(const CoordinateSystem3D& plane)
     {
       if (!source_.GetVolume().HasGeometry() ||
           source_.GetVolume().GetSlicesCount() == 0)
@@ -1834,9 +661,9 @@
         return;
       }
 
-      const OrthancStone::VolumeImageGeometry& geometry = source_.GetVolume().GetImage().GetGeometry();
+      const VolumeImageGeometry& geometry = source_.GetVolume().GetGeometry();
 
-      OrthancStone::VolumeProjection projection;
+      VolumeProjection projection;
       unsigned int sliceIndex;
       if (!geometry.DetectSlice(projection, sliceIndex, plane))
       {
@@ -1847,7 +674,7 @@
       }
 
       uint64_t sliceRevision;
-      if (projection == OrthancStone::VolumeProjection_Axial)
+      if (projection == VolumeProjection_Axial)
       {
         sliceRevision = source_.GetVolume().GetSliceRevision(sliceIndex);
 
@@ -1878,17 +705,17 @@
         lastSliceIndex_ = sliceIndex;
         lastSliceRevision_ = sliceRevision;
 
-        std::auto_ptr<OrthancStone::TextureBaseSceneLayer> texture;
+        std::auto_ptr<TextureBaseSceneLayer> texture;
         
         {
           const DicomInstanceParameters& parameters = source_.GetVolume().GetSliceParameters
-            (projection == OrthancStone::VolumeProjection_Axial ? sliceIndex : 0);
+            (projection == VolumeProjection_Axial ? sliceIndex : 0);
 
-          OrthancStone::ImageBuffer3D::SliceReader reader(source_.GetVolume().GetImage(), projection, sliceIndex);
+          ImageBuffer3D::SliceReader reader(source_.GetVolume().GetImage(), projection, sliceIndex);
           texture.reset(parameters.CreateTexture(reader.GetAccessor()));
         }
 
-        const OrthancStone::CoordinateSystem3D& system = geometry.GetProjectionGeometry(projection);
+        const CoordinateSystem3D& system = geometry.GetProjectionGeometry(projection);
 
         double x0, y0, x1, y1;
         system.ProjectPoint(x0, y0, system.GetOrigin());
@@ -1897,13 +724,13 @@
 
         double dx = x1 - x0;
         double dy = y1 - y0;
-        if (!OrthancStone::LinearAlgebra::IsCloseToZero(dx) ||
-            !OrthancStone::LinearAlgebra::IsCloseToZero(dy))
+        if (!LinearAlgebra::IsCloseToZero(dx) ||
+            !LinearAlgebra::IsCloseToZero(dy))
         {
           texture->SetAngle(atan2(dy, dx));
         }
         
-        OrthancStone::Vector tmp;
+        Vector tmp;
         geometry.GetVoxelDimensions(projection);
         texture->SetPixelSpacing(tmp[0], tmp[1]);
 
@@ -1913,350 +740,14 @@
       }
     }
   };
-  
-  
-
-
-
-  class NativeOracle : public IOracle
-  {
-  private:
-    class Item : public Orthanc::IDynamicObject
-    {
-    private:
-      const OrthancStone::IObserver&  receiver_;
-      std::auto_ptr<IOracleCommand>   command_;
-
-    public:
-      Item(const OrthancStone::IObserver& receiver,
-           IOracleCommand* command) :
-        receiver_(receiver),
-        command_(command)
-      {
-        if (command == NULL)
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-        }
-      }
-
-      const OrthancStone::IObserver& GetReceiver() const
-      {
-        return receiver_;
-      }
-
-      const IOracleCommand& GetCommand() const
-      {
-        assert(command_.get() != NULL);
-        return *command_;
-      }
-    };
-
-
-    enum State
-    {
-      State_Setup,
-      State_Running,
-      State_Stopped
-    };
-
-
-    IMessageEmitter&               emitter_;
-    Orthanc::WebServiceParameters  orthanc_;
-    Orthanc::SharedMessageQueue    queue_;
-    State                          state_;
-    boost::mutex                   mutex_;
-    std::vector<boost::thread*>    workers_;
-
-
-    void CopyHttpHeaders(Orthanc::HttpClient& client,
-                         const HttpHeaders& headers)
-    {
-      for (HttpHeaders::const_iterator it = headers.begin(); it != headers.end(); it++ )
-      {
-        client.AddHeader(it->first, it->second);
-      }
-    }
-
-
-    void DecodeAnswer(std::string& answer,
-                      const HttpHeaders& headers)
-    {
-      Orthanc::HttpCompression contentEncoding = Orthanc::HttpCompression_None;
-
-      for (HttpHeaders::const_iterator it = headers.begin(); 
-           it != headers.end(); ++it)
-      {
-        std::string s;
-        Orthanc::Toolbox::ToLowerCase(s, it->first);
-
-        if (s == "content-encoding")
-        {
-          if (it->second == "gzip")
-          {
-            contentEncoding = Orthanc::HttpCompression_Gzip;
-          }
-          else 
-          {
-            throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol,
-                                            "Unsupported HTTP Content-Encoding: " + it->second);
-          }
-
-          break;
-        }
-      }
-
-      if (contentEncoding == Orthanc::HttpCompression_Gzip)
-      {
-        std::string compressed;
-        answer.swap(compressed);
-          
-        Orthanc::GzipCompressor compressor;
-        compressor.Uncompress(answer, compressed.c_str(), compressed.size());
-      }
-    }
-
-
-    void Execute(const OrthancStone::IObserver& receiver,
-                 const OrthancRestApiCommand& command)
-    {
-      Orthanc::HttpClient client(orthanc_, command.GetUri());
-      client.SetMethod(command.GetMethod());
-      client.SetTimeout(command.GetTimeout());
-
-      CopyHttpHeaders(client, command.GetHttpHeaders());
-
-      if (command.GetMethod() == Orthanc::HttpMethod_Post ||
-          command.GetMethod() == Orthanc::HttpMethod_Put)
-      {
-        client.SetBody(command.GetBody());
-      }
-
-      std::string answer;
-      HttpHeaders answerHeaders;
-      client.ApplyAndThrowException(answer, answerHeaders);
-
-      DecodeAnswer(answer, answerHeaders);
-
-      OrthancRestApiCommand::SuccessMessage message(command, answerHeaders, answer);
-      emitter_.EmitMessage(receiver, message);
-    }
-
-
-    void Execute(const OrthancStone::IObserver& receiver,
-                 const GetOrthancImageCommand& command)
-    {
-      Orthanc::HttpClient client(orthanc_, command.GetUri());
-      client.SetTimeout(command.GetTimeout());
-
-      CopyHttpHeaders(client, command.GetHttpHeaders());
-
-      std::string answer;
-      HttpHeaders answerHeaders;
-      client.ApplyAndThrowException(answer, answerHeaders);
-
-      DecodeAnswer(answer, answerHeaders);
-
-      command.ProcessHttpAnswer(emitter_, receiver, answer, answerHeaders);
-    }
-
-
-    void Execute(const OrthancStone::IObserver& receiver,
-                 const GetOrthancWebViewerJpegCommand& command)
-    {
-      Orthanc::HttpClient client(orthanc_, command.GetUri());
-      client.SetTimeout(command.GetTimeout());
-
-      CopyHttpHeaders(client, command.GetHttpHeaders());
-
-      std::string answer;
-      HttpHeaders answerHeaders;
-      client.ApplyAndThrowException(answer, answerHeaders);
-
-      DecodeAnswer(answer, answerHeaders);
-
-      command.ProcessHttpAnswer(emitter_, receiver, answer);
-    }
-
-
-    void Step()
-    {
-      std::auto_ptr<Orthanc::IDynamicObject>  object(queue_.Dequeue(100));
-
-      if (object.get() != NULL)
-      {
-        const Item& item = dynamic_cast<Item&>(*object);
-
-        try
-        {
-          switch (item.GetCommand().GetType())
-          {
-            case IOracleCommand::Type_OrthancRestApi:
-              Execute(item.GetReceiver(), 
-                      dynamic_cast<const OrthancRestApiCommand&>(item.GetCommand()));
-              break;
-
-            case IOracleCommand::Type_GetOrthancImage:
-              Execute(item.GetReceiver(), 
-                      dynamic_cast<const GetOrthancImageCommand&>(item.GetCommand()));
-              break;
-
-            case IOracleCommand::Type_GetOrthancWebViewerJpeg:
-              Execute(item.GetReceiver(), 
-                      dynamic_cast<const GetOrthancWebViewerJpegCommand&>(item.GetCommand()));
-              break;
-
-            default:
-              throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
-          }
-        }
-        catch (Orthanc::OrthancException& e)
-        {
-          LOG(ERROR) << "Exception within the oracle: " << e.What();
-          emitter_.EmitMessage(item.GetReceiver(), OracleCommandExceptionMessage(item.GetCommand(), e));
-        }
-        catch (...)
-        {
-          LOG(ERROR) << "Native exception within the oracle";
-          emitter_.EmitMessage(item.GetReceiver(), OracleCommandExceptionMessage
-                               (item.GetCommand(), Orthanc::ErrorCode_InternalError));
-        }
-      }
-    }
-
-
-    static void Worker(NativeOracle* that)
-    {
-      assert(that != NULL);
-      
-      for (;;)
-      {
-        {
-          boost::mutex::scoped_lock lock(that->mutex_);
-          if (that->state_ != State_Running)
-          {
-            return;
-          }
-        }
-
-        that->Step();
-      }
-    }
-
-
-    void StopInternal()
-    {
-      {
-        boost::mutex::scoped_lock lock(mutex_);
-
-        if (state_ == State_Setup ||
-            state_ == State_Stopped)
-        {
-          return;
-        }
-        else
-        {
-          state_ = State_Stopped;
-        }
-      }
-
-      for (size_t i = 0; i < workers_.size(); i++)
-      {
-        if (workers_[i] != NULL)
-        {
-          if (workers_[i]->joinable())
-          {
-            workers_[i]->join();
-          }
-
-          delete workers_[i];
-        }
-      } 
-    }
-
-
-  public:
-    NativeOracle(IMessageEmitter& emitter) :
-      emitter_(emitter),
-      state_(State_Setup),
-      workers_(4)
-    {
-    }
-
-    virtual ~NativeOracle()
-    {
-      StopInternal();
-    }
-
-    void SetOrthancParameters(const Orthanc::WebServiceParameters& orthanc)
-    {
-      boost::mutex::scoped_lock lock(mutex_);
-
-      if (state_ != State_Setup)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-      }
-      else
-      {
-        orthanc_ = orthanc;
-      }
-    }
-
-    void SetWorkersCount(unsigned int count)
-    {
-      boost::mutex::scoped_lock lock(mutex_);
-
-      if (count <= 0)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-      }
-      else if (state_ != State_Setup)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-      }
-      else
-      {
-        workers_.resize(count);
-      }
-    }
-
-    void Start()
-    {
-      boost::mutex::scoped_lock lock(mutex_);
-
-      if (state_ != State_Setup)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
-      }
-      else
-      {
-        state_ = State_Running;
-
-        for (unsigned int i = 0; i < workers_.size(); i++)
-        {
-          workers_[i] = new boost::thread(Worker, this);
-        }
-      }      
-    }
-
-    void Stop()
-    {
-      StopInternal();
-    }
-
-    virtual void Schedule(const OrthancStone::IObserver& receiver,
-                          IOracleCommand* command)
-    {
-      queue_.Enqueue(new Item(receiver, command));
-    }
-  };
 
 
   class NativeApplicationContext : public IMessageEmitter
   {
   private:
     boost::shared_mutex            mutex_;
-    OrthancStone::MessageBroker    broker_;
-    OrthancStone::IObservable      oracleObservable_;
+    MessageBroker    broker_;
+    IObservable      oracleObservable_;
 
   public:
     NativeApplicationContext() :
@@ -2265,8 +756,8 @@
     }
 
 
-    virtual void EmitMessage(const OrthancStone::IObserver& observer,
-                             const OrthancStone::IMessage& message)
+    virtual void EmitMessage(const IObserver& observer,
+                             const IMessage& message)
     {
       try
       {
@@ -2288,8 +779,8 @@
 
     public:
       ReaderLock(NativeApplicationContext& that) : 
-        that_(that),
-        lock_(that.mutex_)
+      that_(that),
+      lock_(that.mutex_)
       {
       }
     };
@@ -2303,17 +794,17 @@
 
     public:
       WriterLock(NativeApplicationContext& that) : 
-        that_(that),
-        lock_(that.mutex_)
+      that_(that),
+      lock_(that.mutex_)
       {
       }
 
-      OrthancStone::MessageBroker& GetBroker() 
+      MessageBroker& GetBroker() 
       {
         return that_.broker_;
       }
 
-      OrthancStone::IObservable& GetOracleObservable()
+      IObservable& GetOracleObservable()
       {
         return that_.oracleObservable_;
       }
@@ -2326,7 +817,12 @@
 class Toto : public OrthancStone::IObserver
 {
 private:
-  void Handle(const Refactoring::OrthancRestApiCommand::SuccessMessage& message)
+  void Handle(const OrthancStone::SleepOracleCommand::TimeoutMessage& message)
+  {
+    printf("TIMEOUT! %d\n", dynamic_cast<const Orthanc::SingleValueObject<unsigned int>& >(message.GetOrigin().GetPayload()).GetValue());
+  }
+
+  void Handle(const OrthancStone::OrthancRestApiCommand::SuccessMessage& message)
   {
     Json::Value v;
     message.ParseJsonBody(v);
@@ -2334,24 +830,24 @@
     printf("ICI [%s]\n", v.toStyledString().c_str());
   }
 
-  void Handle(const Refactoring::GetOrthancImageCommand::SuccessMessage& message)
+  void Handle(const OrthancStone::GetOrthancImageCommand::SuccessMessage& message)
   {
     printf("IMAGE %dx%d\n", message.GetImage().GetWidth(), message.GetImage().GetHeight());
   }
 
-  void Handle(const Refactoring::GetOrthancWebViewerJpegCommand::SuccessMessage& message)
+  void Handle(const OrthancStone::GetOrthancWebViewerJpegCommand::SuccessMessage& message)
   {
     printf("WebViewer %dx%d\n", message.GetImage().GetWidth(), message.GetImage().GetHeight());
   }
 
-  void Handle(const Refactoring::OracleCommandExceptionMessage& message)
+  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 Refactoring::IOracleCommand::Type_GetOrthancWebViewerJpeg:
-        printf("URI: [%s]\n", dynamic_cast<const Refactoring::GetOrthancWebViewerJpegCommand&>
+      case OrthancStone::IOracleCommand::Type_GetOrthancWebViewerJpeg:
+        printf("URI: [%s]\n", dynamic_cast<const OrthancStone::GetOrthancWebViewerJpegCommand&>
                (message.GetCommand()).GetUri().c_str());
         break;
       
@@ -2366,43 +862,47 @@
   {
     oracle.RegisterObserverCallback
       (new OrthancStone::Callable
-       <Toto, Refactoring::OrthancRestApiCommand::SuccessMessage>(*this, &Toto::Handle));
+       <Toto, OrthancStone::SleepOracleCommand::TimeoutMessage>(*this, &Toto::Handle));
+
+    oracle.RegisterObserverCallback
+      (new OrthancStone::Callable
+       <Toto, OrthancStone::OrthancRestApiCommand::SuccessMessage>(*this, &Toto::Handle));
 
     oracle.RegisterObserverCallback
       (new OrthancStone::Callable
-       <Toto, Refactoring::GetOrthancImageCommand::SuccessMessage>(*this, &Toto::Handle));
+       <Toto, OrthancStone::GetOrthancImageCommand::SuccessMessage>(*this, &Toto::Handle));
 
     oracle.RegisterObserverCallback
       (new OrthancStone::Callable
-       <Toto, Refactoring::GetOrthancWebViewerJpegCommand::SuccessMessage>(*this, &Toto::Handle));
+       <Toto, OrthancStone::GetOrthancWebViewerJpegCommand::SuccessMessage>(*this, &Toto::Handle));
 
     oracle.RegisterObserverCallback
       (new OrthancStone::Callable
-       <Toto, Refactoring::OracleCommandExceptionMessage>(*this, &Toto::Handle));
+       <Toto, OrthancStone::OracleCommandExceptionMessage>(*this, &Toto::Handle));
   }
 };
 
 
-void Run(Refactoring::NativeApplicationContext& context,
-         Refactoring::IOracle& oracle)
+void Run(OrthancStone::NativeApplicationContext& context,
+         OrthancStone::IOracle& oracle)
 {
   std::auto_ptr<Toto> toto;
-  std::auto_ptr<Refactoring::VolumeSeriesOrthancLoader> loader1, loader2;
+  std::auto_ptr<OrthancStone::VolumeSeriesOrthancLoader> loader1, loader2;
 
   {
-    Refactoring::NativeApplicationContext::WriterLock lock(context);
+    OrthancStone::NativeApplicationContext::WriterLock lock(context);
     toto.reset(new Toto(lock.GetOracleObservable()));
-    loader1.reset(new Refactoring::VolumeSeriesOrthancLoader(oracle, lock.GetOracleObservable()));
-    loader2.reset(new Refactoring::VolumeSeriesOrthancLoader(oracle, lock.GetOracleObservable()));
+    loader1.reset(new OrthancStone::VolumeSeriesOrthancLoader(oracle, lock.GetOracleObservable()));
+    loader2.reset(new OrthancStone::VolumeSeriesOrthancLoader(oracle, lock.GetOracleObservable()));
   }
 
-  if (1)
+  if (0)
   {
     Json::Value v = Json::objectValue;
     v["Level"] = "Series";
     v["Query"] = Json::objectValue;
 
-    std::auto_ptr<Refactoring::OrthancRestApiCommand>  command(new Refactoring::OrthancRestApiCommand);
+    std::auto_ptr<OrthancStone::OrthancRestApiCommand>  command(new OrthancStone::OrthancRestApiCommand);
     command->SetMethod(Orthanc::HttpMethod_Post);
     command->SetUri("/tools/find");
     command->SetBody(v);
@@ -2410,50 +910,50 @@
     oracle.Schedule(*toto, command.release());
   }
   
-  if (1)
+  if (0)
   {
-    std::auto_ptr<Refactoring::GetOrthancImageCommand>  command(new Refactoring::GetOrthancImageCommand);
+    std::auto_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 (1)
+  if (0)
   {
-    std::auto_ptr<Refactoring::GetOrthancImageCommand>  command(new Refactoring::GetOrthancImageCommand);
+    std::auto_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 (1)
+  if (0)
   {
-    std::auto_ptr<Refactoring::GetOrthancImageCommand>  command(new Refactoring::GetOrthancImageCommand);
+    std::auto_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 (1)
+  if (0)
   {
-    std::auto_ptr<Refactoring::GetOrthancImageCommand>  command(new Refactoring::GetOrthancImageCommand);
+    std::auto_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 (1)
+  if (0)
   {
-    std::auto_ptr<Refactoring::GetOrthancImageCommand>  command(new Refactoring::GetOrthancImageCommand);
+    std::auto_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 (1)
+  if (0)
   {
-    std::auto_ptr<Refactoring::GetOrthancWebViewerJpegCommand>  command(new Refactoring::GetOrthancWebViewerJpegCommand);
+    std::auto_ptr<OrthancStone::GetOrthancWebViewerJpegCommand>  command(new OrthancStone::GetOrthancWebViewerJpegCommand);
     command->SetHttpHeader("Accept-Encoding", "gzip");
     command->SetInstance("e6c7c20b-c9f65d7e-0d76f2e2-830186f2-3e3c600e");
     command->SetQuality(90);
@@ -2461,12 +961,23 @@
   }
 
 
+  if (0)
+  {
+    for (unsigned int i = 0; i < 10; i++)
+    {
+      std::auto_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
   //loader1->LoadSeries("cb3ea4d1-d08f3856-ad7b6314-74d88d77-60b05618");  // CT
   //loader2->LoadInstance("41029085-71718346-811efac4-420e2c15-d39f99b6");  // RT-DOSE
 
   // Delphine
-  loader1->LoadSeries("5990e39c-51e5f201-fe87a54c-31a55943-e59ef80e");  // CT
+  //loader1->LoadSeries("5990e39c-51e5f201-fe87a54c-31a55943-e59ef80e");  // CT
+  loader1->LoadSeries("67f1b334-02c16752-45026e40-a5b60b6b-030ecab5");  // Lung 1/10mm
 
   LOG(WARNING) << "...Waiting for Ctrl-C...";
   Orthanc::SystemToolbox::ServerBarrier();
@@ -2487,9 +998,9 @@
 
   try
   {
-    Refactoring::NativeApplicationContext context;
+    OrthancStone::NativeApplicationContext context;
 
-    Refactoring::NativeOracle oracle(context);
+    OrthancStone::ThreadedOracle oracle(context);
 
     {
       Orthanc::WebServiceParameters p;
--- a/Samples/Sdl/TrackerSampleApp.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Samples/Sdl/TrackerSampleApp.cpp	Wed May 22 16:13:46 2019 +0200
@@ -20,20 +20,18 @@
 
 #include "TrackerSampleApp.h"
 
-#include <Framework/Scene2DViewport/CreateLineMeasureTracker.h>
-#include <Framework/Scene2DViewport/CreateAngleMeasureTracker.h>
+#include "../../Applications/Sdl/SdlOpenGLWindow.h"
 
-#include <Framework/Scene2D/PanSceneTracker.h>
-#include <Framework/Scene2D/RotateSceneTracker.h>
-#include <Framework/Scene2D/Scene2D.h>
-#include <Framework/Scene2D/ZoomSceneTracker.h>
-#include <Framework/Scene2D/CairoCompositor.h>
-#include <Framework/Scene2D/ColorTextureSceneLayer.h>
-#include <Framework/Scene2D/OpenGLCompositor.h>
-
-#include <Framework/StoneInitialization.h>
-
-#include <Applications/Sdl/SdlOpenGLWindow.h>
+#include "../../Framework/Scene2D/CairoCompositor.h"
+#include "../../Framework/Scene2D/ColorTextureSceneLayer.h"
+#include "../../Framework/Scene2D/OpenGLCompositor.h"
+#include "../../Framework/Scene2D/PanSceneTracker.h"
+#include "../../Framework/Scene2D/RotateSceneTracker.h"
+#include "../../Framework/Scene2D/Scene2D.h"
+#include "../../Framework/Scene2D/ZoomSceneTracker.h"
+#include "../../Framework/Scene2DViewport/CreateAngleMeasureTracker.h"
+#include "../../Framework/Scene2DViewport/CreateLineMeasureTracker.h"
+#include "../../Framework/StoneInitialization.h"
 
 // From Orthanc framework
 #include <Core/Logging.h>
--- a/Samples/Sdl/TrackerSampleApp.h	Wed May 22 16:01:34 2019 +0200
+++ b/Samples/Sdl/TrackerSampleApp.h	Wed May 22 16:13:46 2019 +0200
@@ -18,15 +18,13 @@
  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  **/
 
-#include <Framework/Scene2DViewport/PointerTypes.h>
 
-#include <Framework/Messages/IObserver.h>
-
-#include <Framework/Scene2D/OpenGLCompositor.h>
-
-#include <Framework/Scene2DViewport/ViewportController.h>
-#include <Framework/Scene2DViewport/IFlexiblePointerTracker.h>
-#include <Framework/Scene2DViewport/MeasureTools.h>
+#include "../../Framework/Messages/IObserver.h"
+#include "../../Framework/Scene2D/OpenGLCompositor.h"
+#include "../../Framework/Scene2DViewport/IFlexiblePointerTracker.h"
+#include "../../Framework/Scene2DViewport/MeasureTools.h"
+#include "../../Framework/Scene2DViewport/PointerTypes.h"
+#include "../../Framework/Scene2DViewport/ViewportController.h"
 
 #include <SDL.h>
 
--- a/Samples/WebAssembly/BasicScene.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/Samples/WebAssembly/BasicScene.cpp	Wed May 22 16:13:46 2019 +0200
@@ -158,8 +158,7 @@
     void SetupEvents(const std::string& canvas);
 
   public:
-    WebAssemblyViewport(MessageBroker& broker,
-                        const std::string& canvas) :
+    WebAssemblyViewport(const std::string& canvas) :
       context_(canvas),
       controller_(make_shared<ViewportController>(broker)),
       compositor_(context_, *controller_->GetScene())
@@ -381,7 +380,6 @@
 std::auto_ptr<OrthancStone::WebAssemblyViewport>  viewport1_;
 std::auto_ptr<OrthancStone::WebAssemblyViewport>  viewport2_;
 std::auto_ptr<OrthancStone::WebAssemblyViewport>  viewport3_;
-OrthancStone::MessageBroker  broker_;
 
 EM_BOOL OnWindowResize(
   int eventType, const EmscriptenUiEvent *uiEvent, void *userData)
--- a/UnitTestsSources/UnitTestsMain.cpp	Wed May 22 16:01:34 2019 +0200
+++ b/UnitTestsSources/UnitTestsMain.cpp	Wed May 22 16:13:46 2019 +0200
@@ -19,14 +19,14 @@
  **/
 
 
-#include "../Framework/dev.h"
 #include "gtest/gtest.h"
 
-#include "../Framework/Layers/FrameRenderer.h"
-#include "../Framework/Toolbox/DownloadStack.h"
+#include "../Framework/Deprecated/Layers/FrameRenderer.h"
+#include "../Framework/Deprecated/Toolbox/DownloadStack.h"
+#include "../Framework/Deprecated/Toolbox/OrthancSlicesLoader.h"
 #include "../Framework/Toolbox/FiniteProjectiveCamera.h"
+#include "../Framework/Toolbox/GeometryToolbox.h"
 #include "../Framework/Toolbox/MessagingToolbox.h"
-#include "../Framework/Toolbox/OrthancSlicesLoader.h"
 #include "../Framework/Volumes/ImageBuffer3D.h"
 #include "../Platforms/Generic/OracleWebService.h"
 
@@ -42,97 +42,6 @@
 #include <boost/math/special_functions/round.hpp>
 
 
-#if 0
-namespace OrthancStone
-{
-  class Tata : public OrthancSlicesLoader::ICallback
-  {
-  public:
-    virtual void NotifyGeometryReady(const OrthancSlicesLoader& loader)
-    {
-      printf(">> %d\n", (int) loader.GetSliceCount());
-
-      for (size_t i = 0; i < loader.GetSliceCount(); i++)
-      {
-        const_cast<OrthancSlicesLoader&>(loader).ScheduleLoadSliceImage(i, SliceImageQuality_FullPng);
-      }
-    }
-
-    virtual void NotifyGeometryError(const OrthancSlicesLoader& loader)
-    {
-      printf("Error\n");
-    }
-
-    virtual void NotifySliceImageReady(const OrthancSlicesLoader& loader,
-                                       unsigned int sliceIndex,
-                                       const Slice& slice,
-                                       std::auto_ptr<Orthanc::ImageAccessor>& image,
-                                       SliceImageQuality quality)
-    {
-      std::auto_ptr<Orthanc::ImageAccessor> tmp(image);
-      printf("Slice OK %dx%d\n", tmp->GetWidth(), tmp->GetHeight());
-    }
-
-    virtual void NotifySliceImageError(const OrthancSlicesLoader& loader,
-                                       unsigned int sliceIndex,
-                                       const Slice& slice,
-                                       SliceImageQuality quality)
-    {
-      printf("ERROR 2\n");
-    }
-  };
-}
-
-
-TEST(Toto, DISABLED_Tutu)
-{
-  OrthancStone::Oracle oracle(4);
-  oracle.Start();
-
-  Orthanc::WebServiceParameters web;
-  //OrthancStone::OrthancAsynchronousWebService orthanc(web, 4);
-  OrthancStone::OracleWebService orthanc(oracle, web);
-  //orthanc.Start();
-
-  OrthancStone::Tata tata;
-  OrthancStone::OrthancSlicesLoader loader(tata, orthanc);
-  loader.ScheduleLoadSeries("c1c4cb95-05e3bd11-8da9f5bb-87278f71-0b2b43f5");
-  //loader.ScheduleLoadSeries("67f1b334-02c16752-45026e40-a5b60b6b-030ecab5");
-
-  //loader.ScheduleLoadInstance("19816330-cb02e1cf-df3a8fe8-bf510623-ccefe9f5", 0);
-
-  /*printf(">> %d\n", loader.GetSliceCount());
-    loader.ScheduleLoadSliceImage(31);*/
-
-  boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
-
-  //orthanc.Stop();
-  oracle.Stop();
-}
-
-
-TEST(Toto, Tata)
-{
-  OrthancStone::Oracle oracle(4);
-  oracle.Start();
-
-  Orthanc::WebServiceParameters web;
-  OrthancStone::OracleWebService orthanc(oracle, web);
-  OrthancStone::OrthancVolumeImage volume(orthanc, true);
-
-  //volume.ScheduleLoadInstance("19816330-cb02e1cf-df3a8fe8-bf510623-ccefe9f5", 0);
-  //volume.ScheduleLoadSeries("318603c5-03e8cffc-a82b6ee1-3ccd3c1e-18d7e3bb"); // COMUNIX PET
-  //volume.ScheduleLoadSeries("7124dba7-09803f33-98b73826-33f14632-ea842d29"); // COMUNIX CT
-  //volume.ScheduleLoadSeries("5990e39c-51e5f201-fe87a54c-31a55943-e59ef80e"); // Delphine sagital
-  volume.ScheduleLoadSeries("6f1b492a-e181e200-44e51840-ef8db55e-af529ab6"); // Delphine ax 2.5
-
-  boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
-
-  oracle.Stop();
-}
-#endif
-
-
 TEST(GeometryToolbox, Interpolation)
 {
   using namespace OrthancStone::GeometryToolbox;