changeset 318:3a4ca166fafa am-2

ImageAccessor refactoring + implemented Image Cache in SmartLoader
author am@osimis.io
date Mon, 08 Oct 2018 17:10:08 +0200
parents b66d13708f40
children daa04d15192c
files Applications/Qt/QCairoWidget.cpp Applications/Samples/SimpleViewerApplication.h Framework/Layers/ColorFrameRenderer.cpp Framework/Layers/GrayscaleFrameRenderer.cpp Framework/Layers/ILayerSource.h Framework/Layers/LayerSourceBase.cpp Framework/Layers/LayerSourceBase.h Framework/Layers/OrthancFrameLayerSource.cpp Framework/Layers/OrthancFrameLayerSource.h Framework/Messages/MessageType.h Framework/SmartLoader.cpp Framework/SmartLoader.h Framework/StoneException.h Framework/Toolbox/OrthancSlicesLoader.cpp Framework/Toolbox/OrthancSlicesLoader.h Framework/Toolbox/Slice.cpp Framework/Toolbox/Slice.h Framework/Viewport/CairoSurface.cpp Framework/Viewport/CairoSurface.h Framework/Viewport/WidgetViewport.cpp Framework/Volumes/ImageBuffer3D.cpp Framework/Volumes/ImageBuffer3D.h Framework/Widgets/CairoWidget.cpp Framework/Widgets/LayerWidget.cpp Framework/Widgets/LayoutWidget.cpp Framework/dev.h
diffstat 26 files changed, 496 insertions(+), 273 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/Qt/QCairoWidget.cpp	Fri Oct 05 11:57:36 2018 +0200
+++ b/Applications/Qt/QCairoWidget.cpp	Mon Oct 08 17:10:08 2018 +0200
@@ -50,7 +50,8 @@
   {
     OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_);
     OrthancStone::IViewport& viewport = context_->GetCentralViewport();
-    Orthanc::ImageAccessor a = surface_.GetAccessor();
+    Orthanc::ImageAccessor a;
+    surface_.GetAccessor(a);
     viewport.Render(a);
     painter.drawImage(0, 0, *image_);
   }
--- a/Applications/Samples/SimpleViewerApplication.h	Fri Oct 05 11:57:36 2018 +0200
+++ b/Applications/Samples/SimpleViewerApplication.h	Mon Oct 08 17:10:08 2018 +0200
@@ -173,9 +173,12 @@
 #if ORTHANC_ENABLE_WASM==1
       class SimpleViewerApplicationAdapter : public WasmPlatformApplicationAdapter
       {
+        SimpleViewerApplication&  viewerApplication_;
+
       public:
         SimpleViewerApplicationAdapter(MessageBroker& broker, SimpleViewerApplication& application)
-          : WasmPlatformApplicationAdapter(broker, application)
+          : WasmPlatformApplicationAdapter(broker, application),
+          viewerApplication_(application)
         {
 
         }
@@ -183,12 +186,12 @@
         virtual void HandleMessageFromWeb(std::string& output, const std::string& input) {
           if (input == "select-tool:line-measure")
           {
-            application.currentTool_ = Tools_LineMeasure;
+            viewerApplication_.currentTool_ = Tools_LineMeasure;
             NotifyStatusUpdateFromCppToWeb("currentTool=line-measure");
           }
           else if (input == "select-tool:circle-measure")
           {
-            application.currentTool_ = Tools_CircleMeasure;
+            viewerApplication_.currentTool_ = Tools_CircleMeasure;
             NotifyStatusUpdateFromCppToWeb("currentTool=circle-measure");
           }
 
@@ -350,7 +353,7 @@
           // if this is the first thumbnail loaded, load the first instance in the mainWidget
           if (mainWidget_->GetLayerCount() == 0)
           {
-            mainWidget_->AddLayer(smartLoader_->GetFrame(instancesIdsPerSeriesId_[seriesId][0], 0));
+            smartLoader_->SetFrameInWidget(*mainWidget_, 0, instancesIdsPerSeriesId_[seriesId][0], 0);
           }
         }
       }
@@ -362,7 +365,7 @@
         thumbnails_.push_back(thumbnailWidget);
         thumbnailsLayout_->AddWidget(thumbnailWidget);
         thumbnailWidget->RegisterObserverCallback(new Callable<SimpleViewerApplication, LayerWidget::GeometryChangedMessage>(*this, &SimpleViewerApplication::OnWidgetGeometryChanged));
-        thumbnailWidget->AddLayer(smartLoader_->GetFrame(instanceId, 0));
+        smartLoader_->SetFrameInWidget(*thumbnailWidget, 0, instanceId, 0);
         thumbnailWidget->SetInteractor(*thumbnailInteractor_);
       }
 
@@ -378,10 +381,7 @@
 
       void SelectSeriesInMainViewport(const std::string& seriesId)
       {
-        mainWidget_->ReplaceLayer(0, smartLoader_->GetFrame(instancesIdsPerSeriesId_[seriesId][0], 0));
-#if ORTHANC_ENABLE_WASM==1
-        NotifyStatusUpdateFromCppToWeb("series-description=" + seriesTags_[seriesId]["MainDicomTags"]["SeriesDescription"].asString());
-#endif
+        smartLoader_->SetFrameInWidget(*mainWidget_, 0, instancesIdsPerSeriesId_[seriesId][0], 0);
       }
 
       virtual void OnPushButton1Clicked() {}
--- a/Framework/Layers/ColorFrameRenderer.cpp	Fri Oct 05 11:57:36 2018 +0200
+++ b/Framework/Layers/ColorFrameRenderer.cpp	Mon Oct 08 17:10:08 2018 +0200
@@ -30,7 +30,8 @@
   {
     std::auto_ptr<CairoSurface> display(new CairoSurface(frame_->GetWidth(), frame_->GetHeight()));
 
-    Orthanc::ImageAccessor target = display->GetAccessor();
+    Orthanc::ImageAccessor target;
+    display->GetAccessor(target);
     Orthanc::ImageProcessing::Convert(target, *frame_);
 
     return display.release();
--- a/Framework/Layers/GrayscaleFrameRenderer.cpp	Fri Oct 05 11:57:36 2018 +0200
+++ b/Framework/Layers/GrayscaleFrameRenderer.cpp	Mon Oct 08 17:10:08 2018 +0200
@@ -52,7 +52,8 @@
       lut = reinterpret_cast<const uint8_t*>(Orthanc::EmbeddedResources::GetFileResourceBuffer(style.lut_));
     }
 
-    Orthanc::ImageAccessor target = result->GetAccessor();
+    Orthanc::ImageAccessor target;
+    result->GetAccessor(target);
     const unsigned int width = target.GetWidth();
     const unsigned int height = target.GetHeight();
     
--- a/Framework/Layers/ILayerSource.h	Fri Oct 05 11:57:36 2018 +0200
+++ b/Framework/Layers/ILayerSource.h	Mon Oct 08 17:10:08 2018 +0200
@@ -25,6 +25,8 @@
 #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
 {
@@ -46,7 +48,7 @@
       }
     };
 
-    struct LayerReadyMessage : public OriginMessage<MessageType_LayerSource_LayerReady,ILayerSource>
+    struct LayerReadyMessage : public OriginMessage<MessageType_LayerSource_LayerReady, ILayerSource>
     {
       std::auto_ptr<ILayerRenderer>& renderer_;
       const CoordinateSystem3D& slice_;
@@ -64,6 +66,25 @@
       {
       }
     };
+
+    struct ImageReadyMessage : public OriginMessage<MessageType_LayerSource_ImageReady, ILayerSource>
+    {
+      boost::shared_ptr<Orthanc::ImageAccessor> image_;
+      SliceImageQuality                         imageQuality_;
+      const Slice&                              slice_;
+
+      ImageReadyMessage(ILayerSource& origin,
+                        boost::shared_ptr<Orthanc::ImageAccessor> image,
+                        SliceImageQuality imageQuality,
+                        const Slice& slice
+                        )
+        : OriginMessage(origin),
+          image_(image),
+          imageQuality_(imageQuality),
+          slice_(slice)
+      {
+      }
+    };
     
     ILayerSource(MessageBroker& broker)
       : IObservable(broker)
--- a/Framework/Layers/LayerSourceBase.cpp	Fri Oct 05 11:57:36 2018 +0200
+++ b/Framework/Layers/LayerSourceBase.cpp	Mon Oct 08 17:10:08 2018 +0200
@@ -53,4 +53,11 @@
     EmitMessage(ILayerSource::LayerReadyMessage(*this, renderer, slice, isError));
   }
 
+  void LayerSourceBase::NotifyImageReady(boost::shared_ptr<Orthanc::ImageAccessor> image,
+                                         SliceImageQuality imageQuality,
+                                         const Slice& slice)
+  {
+    EmitMessage(ILayerSource::ImageReadyMessage(*this, image, imageQuality, slice));
+  }
+
 }
--- a/Framework/Layers/LayerSourceBase.h	Fri Oct 05 11:57:36 2018 +0200
+++ b/Framework/Layers/LayerSourceBase.h	Mon Oct 08 17:10:08 2018 +0200
@@ -26,6 +26,8 @@
 
 namespace OrthancStone
 {
+  class SmartLoader;
+
   class LayerSourceBase : public ILayerSource
   {
   protected:
@@ -41,15 +43,15 @@
                           const CoordinateSystem3D& slice,
                           bool isError);
 
+    void NotifyImageReady(boost::shared_ptr<Orthanc::ImageAccessor> image,
+                          SliceImageQuality imageQuality,
+                          const Slice& slice);
+
     LayerSourceBase(MessageBroker& broker)
       : ILayerSource(broker)
     {
-//      DeclareEmittableMessage(MessageType_LayerSource_GeometryReady);
-//      DeclareEmittableMessage(MessageType_LayerSource_GeometryError);
-//      DeclareEmittableMessage(MessageType_LayerSource_ContentChanged);
-//      DeclareEmittableMessage(MessageType_LayerSource_SliceChanged);
-//      DeclareEmittableMessage(MessageType_LayerSource_LayerReady);
     }
 
+    friend class SmartLoader;
   };
 }
--- a/Framework/Layers/OrthancFrameLayerSource.cpp	Fri Oct 05 11:57:36 2018 +0200
+++ b/Framework/Layers/OrthancFrameLayerSource.cpp	Mon Oct 08 17:10:08 2018 +0200
@@ -51,9 +51,17 @@
 
   void OrthancFrameLayerSource::OnSliceImageReady(const OrthancSlicesLoader::SliceImageReadyMessage& message)
   {
+    // first notify that the image is ready (targeted to, i.e: an image cache)
+    LayerSourceBase::NotifyImageReady(message.image_, message.effectiveQuality_, message.slice_);
+
+    // then notify that the layer is ready for render
     bool isFull = (message.effectiveQuality_ == SliceImageQuality_FullPng || message.effectiveQuality_ == SliceImageQuality_FullPam);
-    LayerSourceBase::NotifyLayerReady(FrameRenderer::CreateRenderer(message.image_.release(), message.slice_, isFull),
+    std::auto_ptr<Orthanc::ImageAccessor> accessor(new Orthanc::ImageAccessor());
+    message.image_->GetReadOnlyAccessor(*accessor);
+
+    LayerSourceBase::NotifyLayerReady(FrameRenderer::CreateRenderer(accessor.release(), message.slice_, isFull),
                                       message.slice_.GetGeometry(), false);
+
   }
 
   void OrthancFrameLayerSource::OnSliceImageError(const OrthancSlicesLoader::SliceImageErrorMessage& message)
--- a/Framework/Layers/OrthancFrameLayerSource.h	Fri Oct 05 11:57:36 2018 +0200
+++ b/Framework/Layers/OrthancFrameLayerSource.h	Mon Oct 08 17:10:08 2018 +0200
@@ -55,6 +55,11 @@
       quality_ = quality;
     }
 
+    SliceImageQuality GetImageQuality() const
+    {
+      return quality_;
+    }
+
     size_t GetSliceCount() const
     {
       return loader_.GetSliceCount();
@@ -75,6 +80,5 @@
     void OnSliceGeometryError(const OrthancSlicesLoader::SliceGeometryErrorMessage& message);
     void OnSliceImageReady(const OrthancSlicesLoader::SliceImageReadyMessage& message);
     void OnSliceImageError(const OrthancSlicesLoader::SliceImageErrorMessage& message);
-//    virtual void HandleMessage(IObservable& from, const IMessage& message);
   };
 }
--- a/Framework/Messages/MessageType.h	Fri Oct 05 11:57:36 2018 +0200
+++ b/Framework/Messages/MessageType.h	Mon Oct 08 17:10:08 2018 +0200
@@ -27,11 +27,12 @@
     MessageType_Widget_GeometryChanged,
     MessageType_Widget_ContentChanged,
 
-    MessageType_LayerSource_GeometryReady,
+    MessageType_LayerSource_GeometryReady,   // instance tags have been loaded
     MessageType_LayerSource_GeometryError,
     MessageType_LayerSource_ContentChanged,
     MessageType_LayerSource_SliceChanged,
-    MessageType_LayerSource_LayerReady,
+    MessageType_LayerSource_ImageReady,      // instance pixels data have been loaded
+    MessageType_LayerSource_LayerReady,      // layer is ready to be rendered
 
     MessageType_SliceLoader_GeometryReady,
     MessageType_SliceLoader_GeometryError,
--- a/Framework/SmartLoader.cpp	Fri Oct 05 11:57:36 2018 +0200
+++ b/Framework/SmartLoader.cpp	Mon Oct 08 17:10:08 2018 +0200
@@ -22,9 +22,69 @@
 #include "SmartLoader.h"
 #include "Layers/OrthancFrameLayerSource.h"
 #include "Messages/MessageForwarder.h"
+#include "Core/Images/Image.h"
+#include "Framework/Widgets/LayerWidget.h"
+#include "Framework/StoneException.h"
+#include "Framework/Layers/FrameRenderer.h"
 
 namespace OrthancStone
 {
+    enum CachedSliceStatus
+    {
+      CachedSliceStatus_Loading,
+      CachedSliceStatus_Loaded
+    };
+
+  class SmartLoader::CachedSlice : public LayerSourceBase
+  {
+  public:
+    unsigned int                    sliceIndex_;
+    std::auto_ptr<Slice>            slice_;
+    boost::shared_ptr<Orthanc::ImageAccessor>   image_;
+    SliceImageQuality               effectiveQuality_;
+    CachedSliceStatus               status_;
+
+  public:
+
+    CachedSlice(MessageBroker& broker)
+      : LayerSourceBase(broker)
+    {}
+
+    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
+      bool isFull = (effectiveQuality_ == SliceImageQuality_FullPng || effectiveQuality_ == SliceImageQuality_FullPam);
+      std::auto_ptr<Orthanc::ImageAccessor> accessor(new Orthanc::ImageAccessor());
+      image_->GetReadOnlyAccessor(*accessor);
+      LayerSourceBase::NotifyLayerReady(FrameRenderer::CreateRenderer(accessor.release(), *slice_, isFull),
+                                        slice_->GetGeometry(), false);
+    }
+
+    CachedSlice* Clone() const
+    {
+      CachedSlice* output = new CachedSlice(broker_);
+      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),
@@ -33,7 +93,7 @@
   {
   }
 
-  ILayerSource* SmartLoader::GetFrame(const std::string& instanceId, unsigned int frame)
+  void SmartLoader::SetFrameInWidget(LayerWidget& layerWidget, 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"
@@ -41,18 +101,49 @@
     // - if currently loading, we need to return an object that will observe the existing LayerSource and forward
     //   the messages to its observables
     // in both cases, we must be carefull about objects lifecycle !!!
-    std::auto_ptr<OrthancFrameLayerSource> layerSource (new OrthancFrameLayerSource(IObserver::broker_, orthancApiClient_));
-    layerSource->SetImageQuality(imageQuality_);
-    layerSource->RegisterObserverCallback(new MessageForwarder<ILayerSource::GeometryReadyMessage>(IObserver::broker_, *this));
-    layerSource->RegisterObserverCallback(new MessageForwarder<ILayerSource::LayerReadyMessage>(IObserver::broker_, *this));
-    layerSource->LoadFrame(instanceId, frame);
+
+    std::auto_ptr<ILayerSource> layerSource;
+
+    if (cachedSlices_.find(instanceId) != cachedSlices_.end() && cachedSlices_[instanceId]->status_ == CachedSliceStatus_Loaded)
+    {
+      layerSource.reset(cachedSlices_[instanceId]->Clone());
+    }
+    else
+    {
+      layerSource.reset(new OrthancFrameLayerSource(IObserver::broker_, orthancApiClient_));
+      dynamic_cast<OrthancFrameLayerSource*>(layerSource.get())->SetImageQuality(imageQuality_);
+      layerSource->RegisterObserverCallback(new Callable<SmartLoader, ILayerSource::GeometryReadyMessage>(*this, &SmartLoader::OnLayerGeometryReady));
+      layerSource->RegisterObserverCallback(new Callable<SmartLoader, ILayerSource::ImageReadyMessage>(*this, &SmartLoader::OnImageReady));
+      layerSource->RegisterObserverCallback(new Callable<SmartLoader, ILayerSource::LayerReadyMessage>(*this, &SmartLoader::OnLayerReady));
+      dynamic_cast<OrthancFrameLayerSource*>(layerSource.get())->LoadFrame(instanceId, frame);
+    }
 
-    return layerSource.release();
+    // make sure that the widget registers the events before we trigger them
+    if (layerWidget.GetLayerCount() == layerIndex)
+    {
+      layerWidget.AddLayer(layerSource.release());
+    }
+    else if (layerWidget.GetLayerCount() > layerIndex)
+    {
+      layerWidget.ReplaceLayer(layerIndex, layerSource.release());
+    }
+    else
+    {
+      throw StoneException(ErrorCode_CanOnlyAddOneLayerAtATime);
+    }
+
+    SmartLoader::CachedSlice* cachedSlice = dynamic_cast<SmartLoader::CachedSlice*>(layerSource.get());
+    if (cachedSlice != NULL)
+    {
+      cachedSlice->NotifyGeometryReady();
+    }
+
   }
 
+
   void SmartLoader::LoadStudyList()
   {
-//    orthancApiClient_.ScheduleGetJsonRequest("/studies");
+    //    orthancApiClient_.ScheduleGetJsonRequest("/studies");
   }
 
   void PreloadStudy(const std::string studyId)
@@ -66,4 +157,48 @@
   }
 
 
+  void SmartLoader::OnLayerGeometryReady(const ILayerSource::GeometryReadyMessage& message)
+  {
+    OrthancFrameLayerSource& source = dynamic_cast<OrthancFrameLayerSource&>(message.origin_);
+    // save the slice
+    const Slice& slice = source.GetSlice(0); // TODO handle GetSliceCount()
+    std::string instanceId = slice.GetOrthancInstanceId();
+
+    CachedSlice* cachedSlice = new CachedSlice(IObserver::broker_);
+    cachedSlice->slice_.reset(slice.Clone());
+    cachedSlice->effectiveQuality_ = source.GetImageQuality();
+    cachedSlice->status_ = CachedSliceStatus_Loading;
+
+    cachedSlices_[instanceId] = boost::shared_ptr<CachedSlice>(cachedSlice);
+
+    // re-emit original Layer message to observers
+    EmitMessage(message);
+  }
+
+  void SmartLoader::OnImageReady(const ILayerSource::ImageReadyMessage& message)
+  {
+    OrthancFrameLayerSource& source = dynamic_cast<OrthancFrameLayerSource&>(message.origin_);
+
+    // save the slice
+    const Slice& slice = source.GetSlice(0); // TODO handle GetSliceCount() ?
+    std::string instanceId = slice.GetOrthancInstanceId();
+
+    boost::shared_ptr<CachedSlice> cachedSlice(new CachedSlice(IObserver::broker_));
+    cachedSlice->image_ = message.image_;
+    cachedSlice->effectiveQuality_ = message.imageQuality_;
+    cachedSlice->slice_.reset(message.slice_.Clone());
+    cachedSlice->status_ = CachedSliceStatus_Loaded;
+
+    cachedSlices_[instanceId] = cachedSlice;
+
+    // re-emit original Layer message to observers
+    EmitMessage(message);
+  }
+
+  void SmartLoader::OnLayerReady(const ILayerSource::LayerReadyMessage& message)
+  {
+    // re-emit original Layer message to observers
+    EmitMessage(message);
+  }
+
 }
--- a/Framework/SmartLoader.h	Fri Oct 05 11:57:36 2018 +0200
+++ b/Framework/SmartLoader.h	Mon Oct 08 17:10:08 2018 +0200
@@ -28,8 +28,16 @@
 
 namespace OrthancStone
 {
-  class SmartLoader : public IObservable, IObserver
+  class LayerWidget;
+
+  class SmartLoader : public IObservable, public IObserver
   {
+    struct CachedSlice;
+
+  protected:
+    typedef std::map<std::string, boost::shared_ptr<SmartLoader::CachedSlice>> CachedSlices;
+    CachedSlices cachedSlices_;
+
     SliceImageQuality     imageQuality_;
     OrthancApiClient&     orthancApiClient_;
 
@@ -44,10 +52,15 @@
 
     void SetImageQuality(SliceImageQuality imageQuality) { imageQuality_ = imageQuality; }
 
-    ILayerSource* GetFrame(const std::string& instanceId, unsigned int frame);
+    void SetFrameInWidget(LayerWidget& layerWidget, size_t layerIndex, const std::string& instanceId, unsigned int frame);
 
     void GetFirstInstanceIdForSeries(std::string& output, const std::string& seriesId);
 
+  private:
+    void OnLayerGeometryReady(const ILayerSource::GeometryReadyMessage& message);
+    void OnImageReady(const ILayerSource::ImageReadyMessage& message);
+    void OnLayerReady(const ILayerSource::LayerReadyMessage& message);
+
   };
 
 }
--- a/Framework/StoneException.h	Fri Oct 05 11:57:36 2018 +0200
+++ b/Framework/StoneException.h	Mon Oct 08 17:10:08 2018 +0200
@@ -36,6 +36,7 @@
     ErrorCode_PromiseSingleSuccessHandler, // a Promise can only have a single success handler
     ErrorCode_PromiseSingleFailureHandler, // a Promise can only have a single failure handler
 
+    ErrorCode_CanOnlyAddOneLayerAtATime,
     ErrorCode_CommandJsonInvalidFormat,
     ErrorCode_Last
   };
--- a/Framework/Toolbox/OrthancSlicesLoader.cpp	Fri Oct 05 11:57:36 2018 +0200
+++ b/Framework/Toolbox/OrthancSlicesLoader.cpp	Mon Oct 08 17:10:08 2018 +0200
@@ -173,7 +173,7 @@
   };
 
   void OrthancSlicesLoader::NotifySliceImageSuccess(const Operation& operation,
-                                                    std::auto_ptr<Orthanc::ImageAccessor>& image)
+                                                    boost::shared_ptr<Orthanc::ImageAccessor> image)
   {
     if (image.get() == NULL)
     {
@@ -341,7 +341,7 @@
   void OrthancSlicesLoader::ParseSliceImagePng(const OrthancApiClient::BinaryResponseReadyMessage& message)
   {
     const Operation& operation = dynamic_cast<const OrthancSlicesLoader::Operation&>(*message.Payload);
-    std::auto_ptr<Orthanc::ImageAccessor>  image;
+    boost::shared_ptr<Orthanc::ImageAccessor>  image;
     
     try
     {
@@ -381,7 +381,7 @@
   void OrthancSlicesLoader::ParseSliceImagePam(const OrthancApiClient::BinaryResponseReadyMessage& message)
   {
     const Operation& operation = dynamic_cast<const OrthancSlicesLoader::Operation&>(*message.Payload);
-    std::auto_ptr<Orthanc::ImageAccessor>  image;
+    boost::shared_ptr<Orthanc::ImageAccessor>  image;
 
     try
     {
@@ -461,7 +461,7 @@
       }
     }
     
-    std::auto_ptr<Orthanc::ImageAccessor> reader;
+    boost::shared_ptr<Orthanc::ImageAccessor> reader;
     
     {
       std::string jpeg;
@@ -548,11 +548,11 @@
     }
     
     // Decode a grayscale JPEG 8bpp image coming from the Web viewer
-    std::auto_ptr<Orthanc::ImageAccessor> image
+    boost::shared_ptr<Orthanc::ImageAccessor> image
         (new Orthanc::Image(expectedFormat, reader->GetWidth(), reader->GetHeight(), false));
 
     Orthanc::ImageProcessing::Convert(*image, *reader);
-    reader.reset(NULL);
+    reader = NULL;
     
     float scaling = static_cast<float>(stretchHigh - stretchLow) / 255.0f;
     
@@ -567,8 +567,7 @@
   
   
   class StringImage :
-      public Orthanc::ImageAccessor,
-      public boost::noncopyable
+      public Orthanc::ImageAccessor
   {
   private:
     std::string  buffer_;
@@ -613,7 +612,7 @@
     {
       // This is the case of RT-DOSE (uint32_t values)
       
-      std::auto_ptr<Orthanc::ImageAccessor> image
+      boost::shared_ptr<Orthanc::ImageAccessor> image
           (new StringImage(Orthanc::PixelFormat_Grayscale32, info.GetWidth(),
                            info.GetHeight(), raw));
       
@@ -637,7 +636,7 @@
              info.GetPhotometricInterpretation() == Orthanc::PhotometricInterpretation_Monochrome2 &&
              raw.size() == info.GetWidth() * info.GetHeight() * 2)
     {
-      std::auto_ptr<Orthanc::ImageAccessor> image
+      boost::shared_ptr<Orthanc::ImageAccessor> image
           (new StringImage(Orthanc::PixelFormat_Grayscale16, info.GetWidth(),
                            info.GetHeight(), raw));
       
--- a/Framework/Toolbox/OrthancSlicesLoader.h	Fri Oct 05 11:57:36 2018 +0200
+++ b/Framework/Toolbox/OrthancSlicesLoader.h	Mon Oct 08 17:10:08 2018 +0200
@@ -13,7 +13,7 @@
  * 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/>.
  **/
@@ -27,6 +27,8 @@
 #include "../Messages/IObservable.h"
 #include <boost/shared_ptr.hpp>
 #include "OrthancApiClient.h"
+#include "Core/Images/Image.h"
+
 
 namespace OrthancStone
 {
@@ -41,13 +43,13 @@
     {
       unsigned int sliceIndex_;
       const Slice& slice_;
-      std::auto_ptr<Orthanc::ImageAccessor>& image_;
+      boost::shared_ptr<Orthanc::ImageAccessor> image_;
       SliceImageQuality effectiveQuality_;
 
       SliceImageReadyMessage(unsigned int sliceIndex,
-                        const Slice& slice,
-                        std::auto_ptr<Orthanc::ImageAccessor>& image,
-                        SliceImageQuality effectiveQuality)
+                             const Slice& slice,
+                             boost::shared_ptr<Orthanc::ImageAccessor> image,
+                             SliceImageQuality effectiveQuality)
         : BaseMessage(),
           sliceIndex_(sliceIndex),
           slice_(slice),
@@ -64,8 +66,8 @@
       SliceImageQuality effectiveQuality_;
 
       SliceImageErrorMessage(unsigned int sliceIndex,
-                        const Slice& slice,
-                        SliceImageQuality effectiveQuality)
+                             const Slice& slice,
+                             SliceImageQuality effectiveQuality)
         : BaseMessage(),
           slice_(slice),
           sliceIndex_(sliceIndex),
@@ -100,8 +102,8 @@
     SlicesSorter  slices_;
 
     void NotifySliceImageSuccess(const Operation& operation,
-                                 std::auto_ptr<Orthanc::ImageAccessor>& image);
-  
+                                 boost::shared_ptr<Orthanc::ImageAccessor> image);
+
     void NotifySliceImageError(const Operation& operation);
 
     void OnGeometryError(const OrthancApiClient::HttpErrorMessage& message);
--- a/Framework/Toolbox/Slice.cpp	Fri Oct 05 11:57:36 2018 +0200
+++ b/Framework/Toolbox/Slice.cpp	Mon Oct 08 17:10:08 2018 +0200
@@ -45,6 +45,28 @@
       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)
--- a/Framework/Toolbox/Slice.h	Fri Oct 05 11:57:36 2018 +0200
+++ b/Framework/Toolbox/Slice.h	Mon Oct 08 17:10:08 2018 +0200
@@ -136,5 +136,7 @@
     void GetExtent(std::vector<Vector>& points) const;
 
     const Orthanc::DicomImageInformation& GetImageInformation() const;
+
+    Slice* Clone() const;
   };
 }
--- a/Framework/Viewport/CairoSurface.cpp	Fri Oct 05 11:57:36 2018 +0200
+++ b/Framework/Viewport/CairoSurface.cpp	Mon Oct 08 17:10:08 2018 +0200
@@ -105,24 +105,23 @@
 
   void CairoSurface::Copy(const CairoSurface& other)
   {
-    Orthanc::ImageAccessor source = other.GetConstAccessor();
-    Orthanc::ImageAccessor target = GetAccessor();
+    Orthanc::ImageAccessor source, target;
+
+    other.GetConstAccessor(source);
+    GetAccessor(target);
+
     Orthanc::ImageProcessing::Copy(target, source);
   }
 
 
-  Orthanc::ImageAccessor CairoSurface::GetConstAccessor() const
+  void CairoSurface::GetConstAccessor(Orthanc::ImageAccessor& target) const
   {
-    Orthanc::ImageAccessor accessor;
-    accessor.AssignReadOnly(Orthanc::PixelFormat_BGRA32, width_, height_, pitch_, buffer_);
-    return accessor;
+    target.AssignReadOnly(Orthanc::PixelFormat_BGRA32, width_, height_, pitch_, buffer_);
   }
 
 
-  Orthanc::ImageAccessor CairoSurface::GetAccessor()
+  void CairoSurface::GetAccessor(Orthanc::ImageAccessor& target)
   {
-    Orthanc::ImageAccessor accessor;
-    accessor.AssignWritable(Orthanc::PixelFormat_BGRA32, width_, height_, pitch_, buffer_);
-    return accessor;
+    target.AssignWritable(Orthanc::PixelFormat_BGRA32, width_, height_, pitch_, buffer_);
   }
 }
--- a/Framework/Viewport/CairoSurface.h	Fri Oct 05 11:57:36 2018 +0200
+++ b/Framework/Viewport/CairoSurface.h	Mon Oct 08 17:10:08 2018 +0200
@@ -98,8 +98,8 @@
       return surface_;
     }
 
-    Orthanc::ImageAccessor GetConstAccessor() const;
+    void GetConstAccessor(Orthanc::ImageAccessor& target) const;
 
-    Orthanc::ImageAccessor GetAccessor();
+    void GetAccessor(Orthanc::ImageAccessor& target);
   };
 }
--- a/Framework/Viewport/WidgetViewport.cpp	Fri Oct 05 11:57:36 2018 +0200
+++ b/Framework/Viewport/WidgetViewport.cpp	Mon Oct 08 17:10:08 2018 +0200
@@ -108,7 +108,8 @@
       return false;
     }
     
-    Orthanc::ImageAccessor background = background_.GetAccessor();
+    Orthanc::ImageAccessor background;
+    background_.GetAccessor(background);
 
     if (backgroundChanged_ &&
         !centralWidget_->Render(background))
--- a/Framework/Volumes/ImageBuffer3D.cpp	Fri Oct 05 11:57:36 2018 +0200
+++ b/Framework/Volumes/ImageBuffer3D.cpp	Mon Oct 08 17:10:08 2018 +0200
@@ -13,7 +13,7 @@
  * 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/>.
  **/
@@ -29,53 +29,47 @@
 
 namespace OrthancStone
 {
-  Orthanc::ImageAccessor ImageBuffer3D::GetAxialSliceAccessor(unsigned int slice,
-                                                              bool readOnly) const
+  void ImageBuffer3D::GetAxialSliceAccessor(Orthanc::ImageAccessor& target,
+                                            unsigned int slice,
+                                            bool readOnly) const
   {
     if (slice >= depth_)
     {
       throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
     }
 
-    Orthanc::ImageAccessor accessor;
-
     if (readOnly)
     {
-      accessor.AssignReadOnly(format_, width_, height_, image_.GetPitch(),
-                              image_.GetConstRow(height_ * (depth_ - 1 - slice)));
+      target.AssignReadOnly(format_, width_, height_, image_.GetPitch(),
+                            image_.GetConstRow(height_ * (depth_ - 1 - slice)));
     }
     else
     {
-      accessor.AssignWritable(format_, width_, height_, image_.GetPitch(),
-                              image_.GetRow(height_ * (depth_ - 1 - slice)));
+      target.AssignWritable(format_, width_, height_, image_.GetPitch(),
+                            image_.GetRow(height_ * (depth_ - 1 - slice)));
     }
-
-    return accessor;
   }
 
 
-  Orthanc::ImageAccessor ImageBuffer3D::GetCoronalSliceAccessor(unsigned int slice,
-                                                                bool readOnly) const
+  void ImageBuffer3D::GetCoronalSliceAccessor(Orthanc::ImageAccessor& target,
+                                              unsigned int slice,
+                                              bool readOnly) const
   {
     if (slice >= height_)
     {
       throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
     }
 
-    Orthanc::ImageAccessor accessor;
-
     if (readOnly)
     {
-      accessor.AssignReadOnly(format_, width_, depth_, image_.GetPitch() * height_,
-                              image_.GetConstRow(slice));
+      target.AssignReadOnly(format_, width_, depth_, image_.GetPitch() * height_,
+                            image_.GetConstRow(slice));
     }
     else
     {
-      accessor.AssignWritable(format_, width_, depth_, image_.GetPitch() * height_,
-                              image_.GetRow(slice));
+      target.AssignWritable(format_, width_, depth_, image_.GetPitch() * height_,
+                            image_.GetRow(slice));
     }
-
-    return accessor;
   }
 
 
@@ -97,7 +91,7 @@
 
       for (unsigned int y = 0; y < height_; y++)
       {
-        const void* source = (reinterpret_cast<const uint8_t*>(image_.GetConstRow(y + z * height_)) + 
+        const void* source = (reinterpret_cast<const uint8_t*>(image_.GetConstRow(y + z * height_)) +
                               bytesPerPixel * slice);
 
         memcpy(target, source, bytesPerPixel);
@@ -163,20 +157,20 @@
     Vector result;
     switch (projection)
     {
-      case VolumeProjection_Axial:
-        result = voxelDimensions_;
-        break;
+    case VolumeProjection_Axial:
+      result = voxelDimensions_;
+      break;
 
-      case VolumeProjection_Coronal:
-        LinearAlgebra::AssignVector(result, voxelDimensions_[0], voxelDimensions_[2], voxelDimensions_[1]);
-        break;
+    case VolumeProjection_Coronal:
+      LinearAlgebra::AssignVector(result, voxelDimensions_[0], voxelDimensions_[2], voxelDimensions_[1]);
+      break;
 
-      case VolumeProjection_Sagittal:
-        LinearAlgebra::AssignVector(result, voxelDimensions_[1], voxelDimensions_[2], voxelDimensions_[0]);
-        break;
+    case VolumeProjection_Sagittal:
+      LinearAlgebra::AssignVector(result, voxelDimensions_[1], voxelDimensions_[2], voxelDimensions_[0]);
+      break;
 
-      default:
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    default:
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
     }
 
     return result;
@@ -189,23 +183,23 @@
   {
     switch (projection)
     {
-      case VolumeProjection_Axial:
-        width = width_;
-        height = height_;
-        break;
+    case VolumeProjection_Axial:
+      width = width_;
+      height = height_;
+      break;
 
-      case VolumeProjection_Coronal:
-        width = width_;
-        height = depth_;
-        break;
+    case VolumeProjection_Coronal:
+      width = width_;
+      height = depth_;
+      break;
 
-      case VolumeProjection_Sagittal:
-        width = height_;
-        height = depth_;
-        break;
+    case VolumeProjection_Sagittal:
+      width = height_;
+      height = depth_;
+      break;
 
-      default:
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    default:
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
     }
   }
 
@@ -216,51 +210,51 @@
 
     switch (projection)
     {
-      case VolumeProjection_Axial:
-        for (unsigned int z = 0; z < depth_; z++)
-        {
-          Vector origin = axialGeometry_.GetOrigin();
-          origin += static_cast<double>(z) * voxelDimensions_[2] * axialGeometry_.GetNormal();
+    case VolumeProjection_Axial:
+      for (unsigned int z = 0; z < depth_; z++)
+      {
+        Vector origin = axialGeometry_.GetOrigin();
+        origin += static_cast<double>(z) * voxelDimensions_[2] * axialGeometry_.GetNormal();
 
-          result->AddSlice(origin, 
-                           axialGeometry_.GetAxisX(), 
-                           axialGeometry_.GetAxisY());
-        }
-        break;
+        result->AddSlice(origin,
+                         axialGeometry_.GetAxisX(),
+                         axialGeometry_.GetAxisY());
+      }
+      break;
 
-      case VolumeProjection_Coronal:
-        for (unsigned int y = 0; y < height_; y++)
-        {
-          Vector origin = axialGeometry_.GetOrigin();
-          origin += static_cast<double>(y) * voxelDimensions_[1] * axialGeometry_.GetAxisY();
-          origin += static_cast<double>(depth_ - 1) * voxelDimensions_[2] * axialGeometry_.GetNormal();
+    case VolumeProjection_Coronal:
+      for (unsigned int y = 0; y < height_; y++)
+      {
+        Vector origin = axialGeometry_.GetOrigin();
+        origin += static_cast<double>(y) * voxelDimensions_[1] * axialGeometry_.GetAxisY();
+        origin += static_cast<double>(depth_ - 1) * voxelDimensions_[2] * axialGeometry_.GetNormal();
 
-          result->AddSlice(origin, 
-                           axialGeometry_.GetAxisX(), 
-                           -axialGeometry_.GetNormal());
-        }
-        break;
+        result->AddSlice(origin,
+                         axialGeometry_.GetAxisX(),
+                         -axialGeometry_.GetNormal());
+      }
+      break;
 
-      case VolumeProjection_Sagittal:
-        for (unsigned int x = 0; x < width_; x++)
-        {
-          Vector origin = axialGeometry_.GetOrigin();
-          origin += static_cast<double>(x) * voxelDimensions_[0] * axialGeometry_.GetAxisX();
-          origin += static_cast<double>(depth_ - 1) * voxelDimensions_[2] * axialGeometry_.GetNormal();
+    case VolumeProjection_Sagittal:
+      for (unsigned int x = 0; x < width_; x++)
+      {
+        Vector origin = axialGeometry_.GetOrigin();
+        origin += static_cast<double>(x) * voxelDimensions_[0] * axialGeometry_.GetAxisX();
+        origin += static_cast<double>(depth_ - 1) * voxelDimensions_[2] * axialGeometry_.GetNormal();
 
-          result->AddSlice(origin, 
-                           axialGeometry_.GetAxisY(), 
-                           -axialGeometry_.GetNormal());
-        }
-        break;
+        result->AddSlice(origin,
+                         axialGeometry_.GetAxisY(),
+                         -axialGeometry_.GetNormal());
+      }
+      break;
 
-      default:
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);          
+    default:
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
     }
 
     return result.release();
   }
-    
+
 
   uint64_t ImageBuffer3D::GetEstimatedMemorySize() const
   {
@@ -278,27 +272,27 @@
     }
 
     float sliceMin, sliceMax;
-      
+
     switch (slice.GetFormat())
     {
-      case Orthanc::PixelFormat_Grayscale8:
-      case Orthanc::PixelFormat_Grayscale16:
-      case Orthanc::PixelFormat_Grayscale32:
-      case Orthanc::PixelFormat_SignedGrayscale16:
-      {
-        int64_t a, b;
-        Orthanc::ImageProcessing::GetMinMaxIntegerValue(a, b, slice);
-        sliceMin = static_cast<float>(a);
-        sliceMax = static_cast<float>(b);
-        break;
-      }
+    case Orthanc::PixelFormat_Grayscale8:
+    case Orthanc::PixelFormat_Grayscale16:
+    case Orthanc::PixelFormat_Grayscale32:
+    case Orthanc::PixelFormat_SignedGrayscale16:
+    {
+      int64_t a, b;
+      Orthanc::ImageProcessing::GetMinMaxIntegerValue(a, b, slice);
+      sliceMin = static_cast<float>(a);
+      sliceMax = static_cast<float>(b);
+      break;
+    }
 
-      case Orthanc::PixelFormat_Float32:
-        Orthanc::ImageProcessing::GetMinMaxFloatValue(sliceMin, sliceMax, slice);
-        break;
+    case Orthanc::PixelFormat_Float32:
+      Orthanc::ImageProcessing::GetMinMaxFloatValue(sliceMin, sliceMax, slice);
+      break;
 
-      default:
-        return;
+    default:
+      return;
     }
 
     if (hasRange_)
@@ -359,21 +353,21 @@
   {
     switch (projection)
     {
-      case VolumeProjection_Axial:
-        accessor_ = that.GetAxialSliceAccessor(slice, true);
-        break;
+    case VolumeProjection_Axial:
+      that.GetAxialSliceAccessor(accessor_, slice, true);
+      break;
 
-      case VolumeProjection_Coronal:
-        accessor_ = that.GetCoronalSliceAccessor(slice, true);
-        break;
+    case VolumeProjection_Coronal:
+      that.GetCoronalSliceAccessor(accessor_, slice, true);
+      break;
 
-      case VolumeProjection_Sagittal:
-        sagittal_.reset(that.ExtractSagittalSlice(slice));
-        accessor_ = *sagittal_;
-        break;
+    case VolumeProjection_Sagittal:
+      sagittal_.reset(that.ExtractSagittalSlice(slice));
+      sagittal_->GetReadOnlyAccessor(accessor_);
+      break;
 
-      default:
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);          
+    default:
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
     }
   }
 
@@ -385,7 +379,7 @@
       if (sagittal_.get() != NULL)
       {
         // TODO
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);          
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
       }
 
       // Update the dynamic range of the underlying image, if
@@ -403,21 +397,21 @@
   {
     switch (projection)
     {
-      case VolumeProjection_Axial:
-        accessor_ = that.GetAxialSliceAccessor(slice, false);
-        break;
+    case VolumeProjection_Axial:
+      that.GetAxialSliceAccessor(accessor_, slice, false);
+      break;
 
-      case VolumeProjection_Coronal:
-        accessor_ = that.GetCoronalSliceAccessor(slice, false);
-        break;
+    case VolumeProjection_Coronal:
+      that.GetCoronalSliceAccessor(accessor_, slice, false);
+      break;
 
-      case VolumeProjection_Sagittal:
-        sagittal_.reset(that.ExtractSagittalSlice(slice));
-        accessor_ = *sagittal_;
-        break;
+    case VolumeProjection_Sagittal:
+      sagittal_.reset(that.ExtractSagittalSlice(slice));
+      sagittal_->GetWriteableAccessor(accessor_);
+      break;
 
-      default:
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);          
+    default:
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
     }
   }
 
@@ -473,11 +467,11 @@
     const CoordinateSystem3D& axial = GetAxialGeometry();
     
     Vector origin = (axial.MapSliceToWorldCoordinates(-0.5 * ps[0], -0.5 * ps[1]) -
-                     0.5 * ps[2] * axial.GetNormal());
+        0.5 * ps[2] * axial.GetNormal());
 
     return (origin +
             axial.GetAxisX() * ps[0] * x * static_cast<double>(GetWidth()) +
-            axial.GetAxisY() * ps[1] * y * static_cast<double>(GetHeight()) +
-            axial.GetNormal() * ps[2] * z * static_cast<double>(GetDepth()));
+        axial.GetAxisY() * ps[1] * y * static_cast<double>(GetHeight()) +
+        axial.GetNormal() * ps[2] * z * static_cast<double>(GetDepth()));
   }
 }
--- a/Framework/Volumes/ImageBuffer3D.h	Fri Oct 05 11:57:36 2018 +0200
+++ b/Framework/Volumes/ImageBuffer3D.h	Mon Oct 08 17:10:08 2018 +0200
@@ -13,7 +13,7 @@
  * 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/>.
  **/
@@ -48,11 +48,13 @@
 
     void ExtendImageRange(const Orthanc::ImageAccessor& slice);
 
-    Orthanc::ImageAccessor GetAxialSliceAccessor(unsigned int slice,
-                                                 bool readOnly) const;
+    void  GetAxialSliceAccessor(Orthanc::ImageAccessor& target,
+                                unsigned int slice,
+                                bool readOnly) const;
 
-    Orthanc::ImageAccessor GetCoronalSliceAccessor(unsigned int slice,
-                                                   bool readOnly) const;
+    void GetCoronalSliceAccessor(Orthanc::ImageAccessor& target,
+                                 unsigned int slice,
+                                 bool readOnly) const;
 
     Orthanc::Image*  ExtractSagittalSlice(unsigned int slice) const;
 
--- a/Framework/Widgets/CairoWidget.cpp	Fri Oct 05 11:57:36 2018 +0200
+++ b/Framework/Widgets/CairoWidget.cpp	Mon Oct 08 17:10:08 2018 +0200
@@ -63,7 +63,9 @@
 
       if (RenderCairo(context))
       {
-        Orthanc::ImageProcessing::Copy(target, surface_.GetAccessor());
+        Orthanc::ImageAccessor source;
+        surface_.GetAccessor(source);
+        Orthanc::ImageProcessing::Copy(target, source);
         return true;
       }
       else
@@ -86,7 +88,8 @@
     }
     else
     {
-      Orthanc::ImageAccessor accessor = surface_.GetAccessor();
+      Orthanc::ImageAccessor accessor;
+      surface_.GetAccessor(accessor);
       Orthanc::ImageProcessing::Copy(accessor, target);
 
       CairoContext context(surface_);
--- a/Framework/Widgets/LayerWidget.cpp	Fri Oct 05 11:57:36 2018 +0200
+++ b/Framework/Widgets/LayerWidget.cpp	Mon Oct 08 17:10:08 2018 +0200
@@ -23,6 +23,7 @@
 
 #include "../Layers/SliceOutlineRenderer.h"
 #include "../Toolbox/GeometryToolbox.h"
+#include "Framework/Layers/FrameRenderer.h"
 
 #include <Core/Logging.h>
 
@@ -589,7 +590,7 @@
       {
         LOG(INFO) << "Renderer ready for layer " << index;
       }
-      
+
       if (message.renderer_.get() != NULL)
       {
         UpdateLayer(index, message.renderer_.release(), message.slice_);
--- a/Framework/Widgets/LayoutWidget.cpp	Fri Oct 05 11:57:36 2018 +0200
+++ b/Framework/Widgets/LayoutWidget.cpp	Mon Oct 08 17:10:08 2018 +0200
@@ -57,7 +57,8 @@
 
     virtual void Render(Orthanc::ImageAccessor& surface)
     {
-      Orthanc::ImageAccessor accessor = surface.GetRegion(left_, top_, width_, height_);
+      Orthanc::ImageAccessor accessor;
+      surface.GetRegion(accessor, left_, top_, width_, height_);
       tracker_->Render(accessor);
     }
 
@@ -140,7 +141,8 @@
       }
       else 
       {
-        Orthanc::ImageAccessor accessor = target.GetRegion(left_, top_, width_, height_);
+        Orthanc::ImageAccessor accessor;
+        target.GetRegion(accessor, left_, top_, width_, height_);
         return widget_->Render(accessor);
       }
     }
@@ -171,7 +173,8 @@
     {
       if (Contains(x, y))
       {
-        Orthanc::ImageAccessor accessor = target.GetRegion(left_, top_, width_, height_);
+        Orthanc::ImageAccessor accessor;
+        target.GetRegion(accessor, left_, top_, width_, height_);
 
         widget_->RenderMouseOver(accessor, x - left_, y - top_);
       }
--- a/Framework/dev.h	Fri Oct 05 11:57:36 2018 +0200
+++ b/Framework/dev.h	Mon Oct 08 17:10:08 2018 +0200
@@ -13,7 +13,7 @@
  * 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/>.
  **/
@@ -41,10 +41,10 @@
 namespace OrthancStone
 {
   // TODO: Handle errors while loading
-  class OrthancVolumeImage : 
-    public SlicedVolumeBase,
-    public OrthancStone::IObserver
-  { 
+  class OrthancVolumeImage :
+      public SlicedVolumeBase,
+      public OrthancStone::IObserver
+  {
   private:
     OrthancSlicesLoader           loader_;
     std::auto_ptr<ImageBuffer3D>  image_;
@@ -64,7 +64,7 @@
     }
 
 
-    static bool IsCompatible(const Slice& a, 
+    static bool IsCompatible(const Slice& a,
                              const Slice& b)
     {
       if (!GeometryToolbox::IsParallel(a.GetGeometry().GetNormal(),
@@ -98,10 +98,10 @@
     }
 
 
-    static double GetDistance(const Slice& a, 
+    static double GetDistance(const Slice& a,
                               const Slice& b)
     {
-      return fabs(a.GetGeometry().ProjectAlongNormal(a.GetGeometry().GetOrigin()) - 
+      return fabs(a.GetGeometry().ProjectAlongNormal(a.GetGeometry().GetOrigin()) -
                   a.GetGeometry().ProjectAlongNormal(b.GetGeometry().GetOrigin()));
     }
 
@@ -174,10 +174,10 @@
     }
 
     virtual void OnSliceImageReady(const OrthancSlicesLoader& loader,
-                                       unsigned int sliceIndex,
-                                       const Slice& slice,
-                                       std::auto_ptr<Orthanc::ImageAccessor>& image,
-                                       SliceImageQuality quality)
+                                   unsigned int sliceIndex,
+                                   const Slice& slice,
+                                   const boost::shared_ptr<Orthanc::ImageAccessor>& image,
+                                   SliceImageQuality quality)
     {
       {
         ImageBuffer3D::SliceWriter writer(*image_, VolumeProjection_Axial, sliceIndex);
@@ -221,9 +221,9 @@
       }; break;
       case MessageType_SliceLoader_ImageError:
       {
-          const OrthancSlicesLoader::SliceImageErrorMessage& msg = dynamic_cast<const OrthancSlicesLoader::SliceImageErrorMessage&>(message);
-          LOG(ERROR) << "Cannot download slice " << msg.sliceIndex_ << " in a volume image";
-          ScheduleSliceDownload();
+        const OrthancSlicesLoader::SliceImageErrorMessage& msg = dynamic_cast<const OrthancSlicesLoader::SliceImageErrorMessage&>(message);
+        LOG(ERROR) << "Cannot download slice " << msg.sliceIndex_ << " in a volume image";
+        ScheduleSliceDownload();
       }; break;
       default:
         VLOG("unhandled message type" << message.GetType());
@@ -233,13 +233,13 @@
   public:
     OrthancVolumeImage(MessageBroker& broker,
                        OrthancApiClient& orthanc,
-                       bool computeRange) : 
+                       bool computeRange) :
       OrthancStone::IObserver(broker),
       loader_(broker, orthanc),
       computeRange_(computeRange),
       pendingSlices_(0)
     {
-        // TODO: replace with new callables loader_.RegisterObserver(*this);
+      // TODO: replace with new callables loader_.RegisterObserver(*this);
     }
 
     void ScheduleLoadSeries(const std::string& seriesId)
@@ -371,7 +371,7 @@
                  axialThickness * axial.GetGeometry().GetNormal());
       
       reference_ = CoordinateSystem3D(origin,
-                                      axial.GetGeometry().GetAxisX(), 
+                                      axial.GetGeometry().GetAxisX(),
                                       -axial.GetGeometry().GetNormal());
     }
 
@@ -393,7 +393,7 @@
                  axialThickness * axial.GetGeometry().GetNormal());
       
       reference_ = CoordinateSystem3D(origin,
-                                      axial.GetGeometry().GetAxisY(), 
+                                      axial.GetGeometry().GetAxisY(),
                                       axial.GetGeometry().GetNormal());
     }
 
@@ -410,20 +410,20 @@
 
       switch (projection)
       {
-        case VolumeProjection_Axial:
-          SetupAxial(volume);
-          break;
+      case VolumeProjection_Axial:
+        SetupAxial(volume);
+        break;
 
-        case VolumeProjection_Coronal:
-          SetupCoronal(volume);
-          break;
+      case VolumeProjection_Coronal:
+        SetupCoronal(volume);
+        break;
 
-        case VolumeProjection_Sagittal:
-          SetupSagittal(volume);
-          break;
+      case VolumeProjection_Sagittal:
+        SetupSagittal(volume);
+        break;
 
-        default:
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+      default:
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
       }
     }
 
@@ -487,8 +487,8 @@
 
 
   class VolumeImageSource :
-    public LayerSourceBase,
-    private ISlicedVolume::IObserver
+      public LayerSourceBase,
+      private ISlicedVolume::IObserver
   {
   private:
     OrthancVolumeImage&                 volume_;
@@ -512,12 +512,12 @@
       
       LayerSourceBase::NotifyGeometryReady();
     }
-      
+
     virtual void NotifyGeometryError(const ISlicedVolume& volume)
     {
       LayerSourceBase::NotifyGeometryError();
     }
-      
+
     virtual void NotifyContentChange(const ISlicedVolume& volume)
     {
       LayerSourceBase::NotifyContentChange();
@@ -546,17 +546,17 @@
 
       switch (projection)
       {
-        case VolumeProjection_Axial:
-          return *axialGeometry_;
+      case VolumeProjection_Axial:
+        return *axialGeometry_;
 
-        case VolumeProjection_Sagittal:
-          return *sagittalGeometry_;
+      case VolumeProjection_Sagittal:
+        return *sagittalGeometry_;
 
-        case VolumeProjection_Coronal:
-          return *coronalGeometry_;
+      case VolumeProjection_Coronal:
+        return *coronalGeometry_;
 
-        default:
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      default:
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
       }
     }
 
@@ -613,7 +613,7 @@
         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));
@@ -650,9 +650,9 @@
 
           std::auto_ptr<Slice> slice(geometry.GetSlice(closest));
           LayerSourceBase::NotifyLayerReady(
-            FrameRenderer::CreateRenderer(frame.release(), *slice, isFullQuality),
-            //new SliceOutlineRenderer(slice),
-            slice->GetGeometry(), false);
+                FrameRenderer::CreateRenderer(frame.release(), *slice, isFullQuality),
+                //new SliceOutlineRenderer(slice),
+                slice->GetGeometry(), false);
           return;
         }
       }
@@ -665,8 +665,8 @@
 
 
   class VolumeImageInteractor :
-    public IWorldSceneInteractor,
-    protected ISlicedVolume::IObserver
+      public IWorldSceneInteractor,
+      protected ISlicedVolume::IObserver
   {
   private:
     LayerWidget&                        widget_;
@@ -687,11 +687,11 @@
         widget_.SetDefaultView();
       }
     }
-      
+
     virtual void NotifyGeometryError(const ISlicedVolume& volume)
     {
     }
-      
+
     virtual void NotifyContentChange(const ISlicedVolume& volume)
     {
     }
@@ -731,19 +731,19 @@
                             IStatusBar* statusBar)
     {
       int scale = (modifiers & KeyboardModifiers_Control ? 10 : 1);
-          
+
       switch (direction)
       {
-        case MouseWheelDirection_Up:
-          OffsetSlice(-scale);
-          break;
+      case MouseWheelDirection_Up:
+        OffsetSlice(-scale);
+        break;
 
-        case MouseWheelDirection_Down:
-          OffsetSlice(scale);
-          break;
+      case MouseWheelDirection_Down:
+        OffsetSlice(scale);
+        break;
 
-        default:
-          break;
+      default:
+        break;
       }
     }
 
@@ -754,15 +754,15 @@
     {
       switch (key)
       {
-        case 's':
-          widget.SetDefaultView();
-          break;
+      case 's':
+        widget.SetDefaultView();
+        break;
 
-        default:
-          break;
+      default:
+        break;
       }
     }
-      
+
   public:
     VolumeImageInteractor(OrthancVolumeImage& volume,
                           LayerWidget& widget,
@@ -807,13 +807,13 @@
           slice = slices_->GetSliceCount() - 1;
         }
 
-        if (slice != static_cast<int>(slice_)) 
+        if (slice != static_cast<int>(slice_))
         {
           SetSlice(slice);
-        }   
+        }
       }
     }
-      
+
     void SetSlice(size_t slice)
     {
       if (slices_.get() != NULL)
@@ -856,7 +856,7 @@
       const CoordinateSystem3D& slice = otherPlane_.GetSlice();
 
       // Compute the line of intersection between the two slices
-      if (!GeometryToolbox::IntersectTwoPlanes(p, d, 
+      if (!GeometryToolbox::IntersectTwoPlanes(p, d,
                                                slice.GetOrigin(), slice.GetNormal(),
                                                viewportSlice.GetOrigin(), viewportSlice.GetNormal()))
       {
@@ -871,7 +871,7 @@
 
         const Extent2D extent = otherPlane_.GetSceneExtent();
         
-        if (GeometryToolbox::ClipLineToRectangle(x1, y1, x2, y2, 
+        if (GeometryToolbox::ClipLineToRectangle(x1, y1, x2, y2,
                                                  x1, y1, x2, y2,
                                                  extent.GetX1(), extent.GetY1(),
                                                  extent.GetX2(), extent.GetY2()))
@@ -884,6 +884,6 @@
           NotifyLayerReady(NULL, reference.GetGeometry(), false);
         }
       }
-    }      
+    }
   };
 }