changeset 35:6465fbd23bce

move
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 19 Dec 2016 11:37:35 +0100
parents a865c7992a87
children 981f0257e080
files Framework/Layers/SingleFrameRendererFactory.cpp Framework/Messaging/MessagingToolbox.cpp Framework/Messaging/MessagingToolbox.h Framework/Toolbox/DicomStructureSet.cpp Framework/Toolbox/MessagingToolbox.cpp Framework/Toolbox/MessagingToolbox.h Framework/Toolbox/OrthancSeriesLoader.cpp Framework/Volumes/VolumeImage.h Resources/CMake/OrthancStone.cmake
diffstat 9 files changed, 554 insertions(+), 554 deletions(-) [+]
line wrap: on
line diff
--- a/Framework/Layers/SingleFrameRendererFactory.cpp	Mon Dec 19 11:34:18 2016 +0100
+++ b/Framework/Layers/SingleFrameRendererFactory.cpp	Mon Dec 19 11:37:35 2016 +0100
@@ -33,7 +33,7 @@
 #include "SingleFrameRendererFactory.h"
 
 #include "FrameRenderer.h"
-#include "../Messaging/MessagingToolbox.h"
+#include "../Toolbox/MessagingToolbox.h"
 #include "../../Resources/Orthanc/Core/OrthancException.h"
 #include "../../Resources/Orthanc/Plugins/Samples/Common/FullOrthancDataset.h"
 #include "../../Resources/Orthanc/Plugins/Samples/Common/DicomDatasetReader.h"
--- a/Framework/Messaging/MessagingToolbox.cpp	Mon Dec 19 11:34:18 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,450 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#include "MessagingToolbox.h"
-
-#include "../../Resources/Orthanc/Core/Images/Image.h"
-#include "../../Resources/Orthanc/Core/Images/ImageProcessing.h"
-#include "../../Resources/Orthanc/Core/Images/JpegReader.h"
-#include "../../Resources/Orthanc/Core/Images/PngReader.h"
-#include "../../Resources/Orthanc/Core/OrthancException.h"
-#include "../../Resources/Orthanc/Core/Toolbox.h"
-#include "../../Resources/Orthanc/Core/Logging.h"
-
-#include <boost/lexical_cast.hpp>
-#include <json/reader.h>
-
-#if defined(__native_client__)
-#  include <boost/math/special_functions/round.hpp>
-#else
-#  include <boost/date_time/posix_time/posix_time.hpp>
-#  include <boost/date_time/microsec_time_clock.hpp>
-#endif
-
-namespace OrthancStone
-{
-  namespace MessagingToolbox
-  {
-#if defined(__native_client__)
-    static pp::Core* core_ = NULL;
-
-    void Timestamp::Initialize(pp::Core* core)
-    {
-      if (core == NULL)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-      }
-
-      core_ = core;
-    }
-
-    Timestamp::Timestamp()
-    {
-      if (core_ == NULL)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
-      }
-
-      time_ = core_->GetTimeTicks();
-    }
-
-    int Timestamp::GetMillisecondsSince(const Timestamp& other)
-    {
-      double difference = time_ - other.time_;
-      return static_cast<int>(boost::math::iround(difference * 1000.0));
-    }
-#else
-    Timestamp::Timestamp()
-    {
-      time_ = boost::posix_time::microsec_clock::local_time();
-    }
-
-    int Timestamp::GetMillisecondsSince(const Timestamp& other)
-    {
-      boost::posix_time::time_duration difference = time_ - other.time_;
-      return static_cast<int>(difference.total_milliseconds());
-    }
-#endif
-
-    static bool ParseVersion(std::string& version,
-                             unsigned int& major,
-                             unsigned int& minor,
-                             unsigned int& patch,
-                             const Json::Value& info)
-    {
-      if (info.type() != Json::objectValue ||
-          !info.isMember("Version") ||
-          info["Version"].type() != Json::stringValue)
-      {
-        return false;
-      }
-
-      version = info["Version"].asString();
-      if (version == "mainline")
-      {
-        // Some arbitrary high values Orthanc versions will never reach ;)
-        major = 999;
-        minor = 999;
-        patch = 999;
-        return true;
-      }
-
-      std::vector<std::string> tokens;
-      Orthanc::Toolbox::TokenizeString(tokens, version, '.');
-      
-      if (tokens.size() != 2 &&
-          tokens.size() != 3)
-      {
-        return false;
-      }
-
-      int a, b, c;
-      try
-      {
-        a = boost::lexical_cast<int>(tokens[0]);
-        b = boost::lexical_cast<int>(tokens[1]);
-
-        if (tokens.size() == 3)
-        {
-          c = boost::lexical_cast<int>(tokens[2]);
-        }
-        else
-        {
-          c = 0;
-        }
-      }
-      catch (boost::bad_lexical_cast&)
-      {
-        return false;
-      }
-
-      if (a < 0 ||
-          b < 0 ||
-          c < 0)
-      {
-        return false;
-      }
-      else
-      {
-        major = static_cast<unsigned int>(a);
-        minor = static_cast<unsigned int>(b);
-        patch = static_cast<unsigned int>(c);
-        return true;
-      }         
-    }
-
-
-    static void ParseJson(Json::Value& target,
-                          const std::string& source)
-    {
-      Json::Reader reader;
-      if (!reader.parse(source, target))
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
-      }
-    }
-
-
-    void RestApiGet(Json::Value& target,
-                    OrthancPlugins::IOrthancConnection& orthanc,
-                    const std::string& uri)
-    {
-      std::string tmp;
-      orthanc.RestApiGet(tmp, uri);
-      ParseJson(target, tmp);
-    }
-
-
-    void RestApiPost(Json::Value& target,
-                     OrthancPlugins::IOrthancConnection& orthanc,
-                     const std::string& uri,
-                     const std::string& body)
-    {
-      std::string tmp;
-      orthanc.RestApiPost(tmp, uri, body);
-      ParseJson(target, tmp);
-    }
-
-
-    bool HasWebViewerInstalled(OrthancPlugins::IOrthancConnection& orthanc)
-    {
-      try
-      {
-        Json::Value json;
-        RestApiGet(json, orthanc, "/plugins/web-viewer");
-        return json.type() == Json::objectValue;
-      }
-      catch (Orthanc::OrthancException&)
-      {
-        return false;
-      }
-    }
-
-
-    bool CheckOrthancVersion(OrthancPlugins::IOrthancConnection& orthanc)
-    {
-      Json::Value json;
-      std::string version;
-      unsigned int major, minor, patch;
-
-      try
-      {
-        RestApiGet(json, orthanc, "/system");
-      }
-      catch (Orthanc::OrthancException&)
-      {
-        LOG(ERROR) << "Cannot connect to your Orthanc server";
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
-      }
-        
-      if (!ParseVersion(version, major, minor, patch, json))
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
-      }
-
-      LOG(WARNING) << "Version of the Orthanc core (must be above 1.1.0): " << version;
-
-      // Stone is only compatible with Orthanc >= 1.1.0, otherwise deadlocks might occur
-      if (major < 1 ||
-          (major == 1 && minor < 1))
-      {
-        return false;
-      }
-
-      try
-      {
-        RestApiGet(json, orthanc, "/plugins/web-viewer");       
-      }
-      catch (Orthanc::OrthancException&)
-      {
-        // The Web viewer is not installed, this is OK
-        LOG(WARNING) << "The Web viewer plugin is not installed, progressive download is disabled";
-        return true;
-      }
-
-      if (!ParseVersion(version, major, minor, patch, json))
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
-      }
-
-      LOG(WARNING) << "Version of the Web viewer plugin (must be above 2.2): " << version;
-
-      return (major >= 3 ||
-              (major == 2 && minor >= 2));
-    }
-
-
-    Orthanc::ImageAccessor* DecodeFrame(OrthancPlugins::IOrthancConnection& orthanc,
-                                        const std::string& instance,
-                                        unsigned int frame,
-                                        Orthanc::PixelFormat targetFormat)
-    {
-      std::string uri = ("instances/" + instance + "/frames/" + 
-                         boost::lexical_cast<std::string>(frame));
-
-      std::string compressed;
-
-      switch (targetFormat)
-      {
-        case Orthanc::PixelFormat_RGB24:
-          orthanc.RestApiGet(compressed, uri + "/preview");
-          break;
-
-        case Orthanc::PixelFormat_Grayscale16:
-          orthanc.RestApiGet(compressed, uri + "/image-uint16");
-          break;
-
-        case Orthanc::PixelFormat_SignedGrayscale16:
-          orthanc.RestApiGet(compressed, uri + "/image-int16");
-          break;
-
-        default:
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-      }
-      
-      std::auto_ptr<Orthanc::PngReader> result(new Orthanc::PngReader);
-      result->ReadFromMemory(compressed);
-
-      if (targetFormat == Orthanc::PixelFormat_SignedGrayscale16)
-      {
-        if (result->GetFormat() == Orthanc::PixelFormat_Grayscale16)
-        {
-          result->SetFormat(Orthanc::PixelFormat_SignedGrayscale16);
-        }
-        else
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
-        }
-      }
-
-      return result.release();
-    }
-
-
-    Orthanc::ImageAccessor* DecodeJpegFrame(OrthancPlugins::IOrthancConnection& orthanc,
-                                            const std::string& instance,
-                                            unsigned int frame,
-                                            unsigned int quality,
-                                            Orthanc::PixelFormat targetFormat)
-    {
-      if (quality <= 0 || 
-          quality > 100)
-      {
-        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>(quality) + 
-                         "-" + instance + "_" + 
-                         boost::lexical_cast<std::string>(frame));
-
-      Json::Value encoded;
-      RestApiGet(encoded, orthanc, uri);
-
-      if (encoded.type() != Json::objectValue ||
-          !encoded.isMember("Orthanc") ||
-          encoded["Orthanc"].type() != Json::objectValue)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
-      }
-
-      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_NetworkProtocol);
-      }          
-
-      bool isSigned = false;
-      bool isStretched = info["Stretched"].asBool();
-
-      if (info.isMember("IsSigned"))
-      {
-        if (info["IsSigned"].type() != Json::booleanValue)
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
-        }          
-        else
-        {
-          isSigned = info["IsSigned"].asBool();
-        }
-      }
-
-      std::string jpeg;
-      Orthanc::Toolbox::DecodeBase64(jpeg, info["PixelData"].asString());
-
-      std::auto_ptr<Orthanc::JpegReader> reader(new Orthanc::JpegReader);
-      reader->ReadFromMemory(jpeg);
-
-      if (reader->GetFormat() == Orthanc::PixelFormat_RGB24)  // This is a color image
-      {
-        if (targetFormat != Orthanc::PixelFormat_RGB24)
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
-        }
-
-        if (isSigned || isStretched)
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
-        }
-        else
-        {
-          return reader.release();
-        }
-      }
-
-      if (reader->GetFormat() != Orthanc::PixelFormat_Grayscale8)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
-      }
-
-      if (!isStretched)
-      {
-        if (targetFormat != reader->GetFormat())
-        {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
-        }
-
-        return reader.release();
-      }
-
-      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_NetworkProtocol);
-      }
-
-      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_NotImplemented);          
-      }
-
-      // Decode a grayscale JPEG 8bpp image coming from the Web viewer
-      std::auto_ptr<Orthanc::ImageAccessor> image
-        (new Orthanc::Image(targetFormat, reader->GetWidth(), reader->GetHeight(), false));
-
-      float scaling = static_cast<float>(stretchHigh - stretchLow) / 255.0f;
-      float offset = static_cast<float>(stretchLow) / scaling;
-      
-      Orthanc::ImageProcessing::Convert(*image, *reader);
-      Orthanc::ImageProcessing::ShiftScale(*image, offset, scaling);
-
-#if 0
-      /*info.removeMember("PixelData");
-        std::cout << info.toStyledString();*/
-      
-      int64_t a, b;
-      Orthanc::ImageProcessing::GetMinMaxValue(a, b, *image);
-      std::cout << stretchLow << "->" << stretchHigh << " = " << a << "->" << b << std::endl;
-#endif
-
-      return image.release();
-    }
-  }
-}
--- a/Framework/Messaging/MessagingToolbox.h	Mon Dec 19 11:34:18 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,99 +0,0 @@
-/**
- * Stone of Orthanc
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * In addition, as a special exception, the copyright holders of this
- * program give permission to link the code of its release with the
- * OpenSSL project's "OpenSSL" library (or with modified versions of it
- * that use the same license as the "OpenSSL" library), and distribute
- * the linked executables. You must obey the GNU General Public License
- * in all respects for all of the code used other than "OpenSSL". If you
- * modify file(s) with this exception, you may extend this exception to
- * your version of the file(s), but you are not obligated to do so. If
- * you do not wish to do so, delete this exception statement from your
- * version. If you delete this exception statement from all source files
- * in the program, then also delete it here.
- * 
- * 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
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- **/
-
-
-#pragma once
-
-#include "../../Resources/Orthanc/Plugins/Samples/Common/IOrthancConnection.h"
-
-#include "../Enumerations.h"
-#include "../../Resources/Orthanc/Core/Images/ImageAccessor.h"
-
-#include <json/value.h>
-
-#if defined(__native_client__)
-#  include <ppapi/cpp/core.h>
-#else
-#  include <boost/date_time/posix_time/ptime.hpp>
-#endif
-
-namespace OrthancStone
-{
-  namespace MessagingToolbox
-  {
-    class Timestamp
-    {
-    private:
-#if defined(__native_client__)
-      PP_TimeTicks   time_;
-#else
-      boost::posix_time::ptime   time_;
-#endif
-
-    public:
-      Timestamp();
-
-#if defined(__native_client__)
-      static void Initialize(pp::Core* core);
-#endif
-
-      int GetMillisecondsSince(const Timestamp& other);
-    };
-
-
-    void RestApiGet(Json::Value& target,
-                    OrthancPlugins::IOrthancConnection& orthanc,
-                    const std::string& uri);
-
-    void RestApiPost(Json::Value& target,
-                     OrthancPlugins::IOrthancConnection& orthanc,
-                     const std::string& uri,
-                     const std::string& body);
-
-    bool HasWebViewerInstalled(OrthancPlugins::IOrthancConnection& orthanc);
-
-    bool CheckOrthancVersion(OrthancPlugins::IOrthancConnection& orthanc);
-
-    // This downloads the image from Orthanc and keeps its pixel
-    // format unchanged (will be either Grayscale8, Grayscale16,
-    // SignedGrayscale16, or RGB24)
-    Orthanc::ImageAccessor* DecodeFrame(OrthancPlugins::IOrthancConnection& orthanc,
-                                        const std::string& instance,
-                                        unsigned int frame,
-                                        Orthanc::PixelFormat targetFormat);
-
-    Orthanc::ImageAccessor* DecodeJpegFrame(OrthancPlugins::IOrthancConnection& orthanc,
-                                            const std::string& instance,
-                                            unsigned int frame,
-                                            unsigned int quality,
-                                            Orthanc::PixelFormat targetFormat);
-  }
-}
--- a/Framework/Toolbox/DicomStructureSet.cpp	Mon Dec 19 11:34:18 2016 +0100
+++ b/Framework/Toolbox/DicomStructureSet.cpp	Mon Dec 19 11:37:35 2016 +0100
@@ -35,7 +35,7 @@
 #include "../../Resources/Orthanc/Core/Logging.h"
 #include "../../Resources/Orthanc/Core/OrthancException.h"
 #include "../../Resources/Orthanc/Plugins/Samples/Common/FullOrthancDataset.h"
-#include "../Messaging/MessagingToolbox.h"
+#include "../Toolbox/MessagingToolbox.h"
 
 #include <stdio.h>
 #include <boost/lexical_cast.hpp>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Toolbox/MessagingToolbox.cpp	Mon Dec 19 11:37:35 2016 +0100
@@ -0,0 +1,450 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "MessagingToolbox.h"
+
+#include "../../Resources/Orthanc/Core/Images/Image.h"
+#include "../../Resources/Orthanc/Core/Images/ImageProcessing.h"
+#include "../../Resources/Orthanc/Core/Images/JpegReader.h"
+#include "../../Resources/Orthanc/Core/Images/PngReader.h"
+#include "../../Resources/Orthanc/Core/OrthancException.h"
+#include "../../Resources/Orthanc/Core/Toolbox.h"
+#include "../../Resources/Orthanc/Core/Logging.h"
+
+#include <boost/lexical_cast.hpp>
+#include <json/reader.h>
+
+#if defined(__native_client__)
+#  include <boost/math/special_functions/round.hpp>
+#else
+#  include <boost/date_time/posix_time/posix_time.hpp>
+#  include <boost/date_time/microsec_time_clock.hpp>
+#endif
+
+namespace OrthancStone
+{
+  namespace MessagingToolbox
+  {
+#if defined(__native_client__)
+    static pp::Core* core_ = NULL;
+
+    void Timestamp::Initialize(pp::Core* core)
+    {
+      if (core == NULL)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      }
+
+      core_ = core;
+    }
+
+    Timestamp::Timestamp()
+    {
+      if (core_ == NULL)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      }
+
+      time_ = core_->GetTimeTicks();
+    }
+
+    int Timestamp::GetMillisecondsSince(const Timestamp& other)
+    {
+      double difference = time_ - other.time_;
+      return static_cast<int>(boost::math::iround(difference * 1000.0));
+    }
+#else
+    Timestamp::Timestamp()
+    {
+      time_ = boost::posix_time::microsec_clock::local_time();
+    }
+
+    int Timestamp::GetMillisecondsSince(const Timestamp& other)
+    {
+      boost::posix_time::time_duration difference = time_ - other.time_;
+      return static_cast<int>(difference.total_milliseconds());
+    }
+#endif
+
+    static bool ParseVersion(std::string& version,
+                             unsigned int& major,
+                             unsigned int& minor,
+                             unsigned int& patch,
+                             const Json::Value& info)
+    {
+      if (info.type() != Json::objectValue ||
+          !info.isMember("Version") ||
+          info["Version"].type() != Json::stringValue)
+      {
+        return false;
+      }
+
+      version = info["Version"].asString();
+      if (version == "mainline")
+      {
+        // Some arbitrary high values Orthanc versions will never reach ;)
+        major = 999;
+        minor = 999;
+        patch = 999;
+        return true;
+      }
+
+      std::vector<std::string> tokens;
+      Orthanc::Toolbox::TokenizeString(tokens, version, '.');
+      
+      if (tokens.size() != 2 &&
+          tokens.size() != 3)
+      {
+        return false;
+      }
+
+      int a, b, c;
+      try
+      {
+        a = boost::lexical_cast<int>(tokens[0]);
+        b = boost::lexical_cast<int>(tokens[1]);
+
+        if (tokens.size() == 3)
+        {
+          c = boost::lexical_cast<int>(tokens[2]);
+        }
+        else
+        {
+          c = 0;
+        }
+      }
+      catch (boost::bad_lexical_cast&)
+      {
+        return false;
+      }
+
+      if (a < 0 ||
+          b < 0 ||
+          c < 0)
+      {
+        return false;
+      }
+      else
+      {
+        major = static_cast<unsigned int>(a);
+        minor = static_cast<unsigned int>(b);
+        patch = static_cast<unsigned int>(c);
+        return true;
+      }         
+    }
+
+
+    static void ParseJson(Json::Value& target,
+                          const std::string& source)
+    {
+      Json::Reader reader;
+      if (!reader.parse(source, target))
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat);
+      }
+    }
+
+
+    void RestApiGet(Json::Value& target,
+                    OrthancPlugins::IOrthancConnection& orthanc,
+                    const std::string& uri)
+    {
+      std::string tmp;
+      orthanc.RestApiGet(tmp, uri);
+      ParseJson(target, tmp);
+    }
+
+
+    void RestApiPost(Json::Value& target,
+                     OrthancPlugins::IOrthancConnection& orthanc,
+                     const std::string& uri,
+                     const std::string& body)
+    {
+      std::string tmp;
+      orthanc.RestApiPost(tmp, uri, body);
+      ParseJson(target, tmp);
+    }
+
+
+    bool HasWebViewerInstalled(OrthancPlugins::IOrthancConnection& orthanc)
+    {
+      try
+      {
+        Json::Value json;
+        RestApiGet(json, orthanc, "/plugins/web-viewer");
+        return json.type() == Json::objectValue;
+      }
+      catch (Orthanc::OrthancException&)
+      {
+        return false;
+      }
+    }
+
+
+    bool CheckOrthancVersion(OrthancPlugins::IOrthancConnection& orthanc)
+    {
+      Json::Value json;
+      std::string version;
+      unsigned int major, minor, patch;
+
+      try
+      {
+        RestApiGet(json, orthanc, "/system");
+      }
+      catch (Orthanc::OrthancException&)
+      {
+        LOG(ERROR) << "Cannot connect to your Orthanc server";
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
+      }
+        
+      if (!ParseVersion(version, major, minor, patch, json))
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
+      }
+
+      LOG(WARNING) << "Version of the Orthanc core (must be above 1.1.0): " << version;
+
+      // Stone is only compatible with Orthanc >= 1.1.0, otherwise deadlocks might occur
+      if (major < 1 ||
+          (major == 1 && minor < 1))
+      {
+        return false;
+      }
+
+      try
+      {
+        RestApiGet(json, orthanc, "/plugins/web-viewer");       
+      }
+      catch (Orthanc::OrthancException&)
+      {
+        // The Web viewer is not installed, this is OK
+        LOG(WARNING) << "The Web viewer plugin is not installed, progressive download is disabled";
+        return true;
+      }
+
+      if (!ParseVersion(version, major, minor, patch, json))
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
+      }
+
+      LOG(WARNING) << "Version of the Web viewer plugin (must be above 2.2): " << version;
+
+      return (major >= 3 ||
+              (major == 2 && minor >= 2));
+    }
+
+
+    Orthanc::ImageAccessor* DecodeFrame(OrthancPlugins::IOrthancConnection& orthanc,
+                                        const std::string& instance,
+                                        unsigned int frame,
+                                        Orthanc::PixelFormat targetFormat)
+    {
+      std::string uri = ("instances/" + instance + "/frames/" + 
+                         boost::lexical_cast<std::string>(frame));
+
+      std::string compressed;
+
+      switch (targetFormat)
+      {
+        case Orthanc::PixelFormat_RGB24:
+          orthanc.RestApiGet(compressed, uri + "/preview");
+          break;
+
+        case Orthanc::PixelFormat_Grayscale16:
+          orthanc.RestApiGet(compressed, uri + "/image-uint16");
+          break;
+
+        case Orthanc::PixelFormat_SignedGrayscale16:
+          orthanc.RestApiGet(compressed, uri + "/image-int16");
+          break;
+
+        default:
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+      }
+      
+      std::auto_ptr<Orthanc::PngReader> result(new Orthanc::PngReader);
+      result->ReadFromMemory(compressed);
+
+      if (targetFormat == Orthanc::PixelFormat_SignedGrayscale16)
+      {
+        if (result->GetFormat() == Orthanc::PixelFormat_Grayscale16)
+        {
+          result->SetFormat(Orthanc::PixelFormat_SignedGrayscale16);
+        }
+        else
+        {
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
+        }
+      }
+
+      return result.release();
+    }
+
+
+    Orthanc::ImageAccessor* DecodeJpegFrame(OrthancPlugins::IOrthancConnection& orthanc,
+                                            const std::string& instance,
+                                            unsigned int frame,
+                                            unsigned int quality,
+                                            Orthanc::PixelFormat targetFormat)
+    {
+      if (quality <= 0 || 
+          quality > 100)
+      {
+        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>(quality) + 
+                         "-" + instance + "_" + 
+                         boost::lexical_cast<std::string>(frame));
+
+      Json::Value encoded;
+      RestApiGet(encoded, orthanc, uri);
+
+      if (encoded.type() != Json::objectValue ||
+          !encoded.isMember("Orthanc") ||
+          encoded["Orthanc"].type() != Json::objectValue)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
+      }
+
+      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_NetworkProtocol);
+      }          
+
+      bool isSigned = false;
+      bool isStretched = info["Stretched"].asBool();
+
+      if (info.isMember("IsSigned"))
+      {
+        if (info["IsSigned"].type() != Json::booleanValue)
+        {
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
+        }          
+        else
+        {
+          isSigned = info["IsSigned"].asBool();
+        }
+      }
+
+      std::string jpeg;
+      Orthanc::Toolbox::DecodeBase64(jpeg, info["PixelData"].asString());
+
+      std::auto_ptr<Orthanc::JpegReader> reader(new Orthanc::JpegReader);
+      reader->ReadFromMemory(jpeg);
+
+      if (reader->GetFormat() == Orthanc::PixelFormat_RGB24)  // This is a color image
+      {
+        if (targetFormat != Orthanc::PixelFormat_RGB24)
+        {
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
+        }
+
+        if (isSigned || isStretched)
+        {
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+        }
+        else
+        {
+          return reader.release();
+        }
+      }
+
+      if (reader->GetFormat() != Orthanc::PixelFormat_Grayscale8)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+      }
+
+      if (!isStretched)
+      {
+        if (targetFormat != reader->GetFormat())
+        {
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol);
+        }
+
+        return reader.release();
+      }
+
+      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_NetworkProtocol);
+      }
+
+      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_NotImplemented);          
+      }
+
+      // Decode a grayscale JPEG 8bpp image coming from the Web viewer
+      std::auto_ptr<Orthanc::ImageAccessor> image
+        (new Orthanc::Image(targetFormat, reader->GetWidth(), reader->GetHeight(), false));
+
+      float scaling = static_cast<float>(stretchHigh - stretchLow) / 255.0f;
+      float offset = static_cast<float>(stretchLow) / scaling;
+      
+      Orthanc::ImageProcessing::Convert(*image, *reader);
+      Orthanc::ImageProcessing::ShiftScale(*image, offset, scaling);
+
+#if 0
+      /*info.removeMember("PixelData");
+        std::cout << info.toStyledString();*/
+      
+      int64_t a, b;
+      Orthanc::ImageProcessing::GetMinMaxValue(a, b, *image);
+      std::cout << stretchLow << "->" << stretchHigh << " = " << a << "->" << b << std::endl;
+#endif
+
+      return image.release();
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Toolbox/MessagingToolbox.h	Mon Dec 19 11:37:35 2016 +0100
@@ -0,0 +1,99 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../../Resources/Orthanc/Plugins/Samples/Common/IOrthancConnection.h"
+
+#include "../Enumerations.h"
+#include "../../Resources/Orthanc/Core/Images/ImageAccessor.h"
+
+#include <json/value.h>
+
+#if defined(__native_client__)
+#  include <ppapi/cpp/core.h>
+#else
+#  include <boost/date_time/posix_time/ptime.hpp>
+#endif
+
+namespace OrthancStone
+{
+  namespace MessagingToolbox
+  {
+    class Timestamp
+    {
+    private:
+#if defined(__native_client__)
+      PP_TimeTicks   time_;
+#else
+      boost::posix_time::ptime   time_;
+#endif
+
+    public:
+      Timestamp();
+
+#if defined(__native_client__)
+      static void Initialize(pp::Core* core);
+#endif
+
+      int GetMillisecondsSince(const Timestamp& other);
+    };
+
+
+    void RestApiGet(Json::Value& target,
+                    OrthancPlugins::IOrthancConnection& orthanc,
+                    const std::string& uri);
+
+    void RestApiPost(Json::Value& target,
+                     OrthancPlugins::IOrthancConnection& orthanc,
+                     const std::string& uri,
+                     const std::string& body);
+
+    bool HasWebViewerInstalled(OrthancPlugins::IOrthancConnection& orthanc);
+
+    bool CheckOrthancVersion(OrthancPlugins::IOrthancConnection& orthanc);
+
+    // This downloads the image from Orthanc and keeps its pixel
+    // format unchanged (will be either Grayscale8, Grayscale16,
+    // SignedGrayscale16, or RGB24)
+    Orthanc::ImageAccessor* DecodeFrame(OrthancPlugins::IOrthancConnection& orthanc,
+                                        const std::string& instance,
+                                        unsigned int frame,
+                                        Orthanc::PixelFormat targetFormat);
+
+    Orthanc::ImageAccessor* DecodeJpegFrame(OrthancPlugins::IOrthancConnection& orthanc,
+                                            const std::string& instance,
+                                            unsigned int frame,
+                                            unsigned int quality,
+                                            Orthanc::PixelFormat targetFormat);
+  }
+}
--- a/Framework/Toolbox/OrthancSeriesLoader.cpp	Mon Dec 19 11:34:18 2016 +0100
+++ b/Framework/Toolbox/OrthancSeriesLoader.cpp	Mon Dec 19 11:37:35 2016 +0100
@@ -32,7 +32,7 @@
 
 #include "OrthancSeriesLoader.h"
 
-#include "../Messaging/MessagingToolbox.h"
+#include "../Toolbox/MessagingToolbox.h"
 #include "../../Resources/Orthanc/Core/Images/Image.h"
 #include "../../Resources/Orthanc/Core/Images/ImageProcessing.h"
 #include "../../Resources/Orthanc/Core/Logging.h"
--- a/Framework/Volumes/VolumeImage.h	Mon Dec 19 11:34:18 2016 +0100
+++ b/Framework/Volumes/VolumeImage.h	Mon Dec 19 11:37:35 2016 +0100
@@ -35,8 +35,8 @@
 #include "ISliceableVolume.h"
 #include "ImageBuffer3D.h"
 #include "../Toolbox/ISeriesLoader.h"
+#include "../Toolbox/MessagingToolbox.h"
 #include "../Toolbox/ObserversRegistry.h"
-#include "../Messaging/MessagingToolbox.h"
 #include "../Layers/ILayerRendererFactory.h"
 
 #include <boost/thread.hpp>
--- a/Resources/CMake/OrthancStone.cmake	Mon Dec 19 11:34:18 2016 +0100
+++ b/Resources/CMake/OrthancStone.cmake	Mon Dec 19 11:37:35 2016 +0100
@@ -171,12 +171,12 @@
   ${ORTHANC_STONE_DIR}/Framework/Layers/SeriesFrameRendererFactory.cpp
   ${ORTHANC_STONE_DIR}/Framework/Layers/SiblingSliceLocationFactory.cpp
   ${ORTHANC_STONE_DIR}/Framework/Layers/SingleFrameRendererFactory.cpp
-  ${ORTHANC_STONE_DIR}/Framework/Messaging/MessagingToolbox.cpp
   ${ORTHANC_STONE_DIR}/Framework/Toolbox/BinarySemaphore.cpp
   ${ORTHANC_STONE_DIR}/Framework/Toolbox/DicomFrameConverter.cpp
   ${ORTHANC_STONE_DIR}/Framework/Toolbox/DicomStructureSet.cpp
   ${ORTHANC_STONE_DIR}/Framework/Toolbox/DownloadStack.cpp
   ${ORTHANC_STONE_DIR}/Framework/Toolbox/GeometryToolbox.cpp
+  ${ORTHANC_STONE_DIR}/Framework/Toolbox/MessagingToolbox.cpp
   ${ORTHANC_STONE_DIR}/Framework/Toolbox/OrthancSeriesLoader.cpp
   ${ORTHANC_STONE_DIR}/Framework/Toolbox/ParallelSlices.cpp
   ${ORTHANC_STONE_DIR}/Framework/Toolbox/ParallelSlicesCursor.cpp