changeset 172:330ecfd96aec

sync
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 10 Oct 2017 12:53:46 +0200
parents b6c55352818c
children 4f0f4f64cff3
files CMakeLists.txt Orthanc/Core/DicomFormat/DicomMap.cpp Orthanc/Core/DicomFormat/DicomMap.h Orthanc/Core/DicomFormat/DicomTag.h Orthanc/Core/DicomFormat/DicomValue.cpp Orthanc/Core/DicomFormat/DicomValue.h Orthanc/Core/Endianness.h Orthanc/Core/Enumerations.cpp Orthanc/Core/Enumerations.h Orthanc/Core/FileStorage/FilesystemStorage.h Orthanc/Core/Images/ImageAccessor.cpp Orthanc/Core/Images/ImageProcessing.cpp Orthanc/Core/Images/ImageProcessing.h Orthanc/Core/PrecompiledHeaders.h Orthanc/Core/SQLite/OrthancSQLiteException.h Orthanc/Core/SQLite/Statement.h Orthanc/Core/Toolbox.cpp Orthanc/Plugins/Samples/GdcmDecoder/GdcmImageDecoder.cpp Orthanc/Plugins/Samples/GdcmDecoder/GdcmImageDecoder.h Orthanc/Resources/CMake/Compiler.cmake Orthanc/Resources/CMake/GoogleTestConfiguration.cmake Plugin/DecodedImageAdapter.cpp Plugin/ViewerToolbox.cpp
diffstat 23 files changed, 873 insertions(+), 39 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Thu Aug 24 19:49:20 2017 +0200
+++ b/CMakeLists.txt	Tue Oct 10 12:53:46 2017 +0200
@@ -37,8 +37,8 @@
 set(USE_SYSTEM_ORTHANC_SDK ON CACHE BOOL "Use the system version of the Orthanc plugin SDK")
 
 # Distribution-specific settings
-set(USE_GTEST_DEBIAN_SOURCE_PACKAGE OFF CACHE BOOL "Use the sources of Google Test shipped with libgtest-dev (Debian only)")
-mark_as_advanced(USE_GTEST_DEBIAN_SOURCE_PACKAGE)
+set(USE_GOOGLE_TEST_DEBIAN_PACKAGE OFF CACHE BOOL "Use the sources of Google Test shipped with libgtest-dev (Debian only)")
+mark_as_advanced(USE_GOOGLE_TEST_DEBIAN_PACKAGE)
 
 set(ORTHANC_ROOT ${CMAKE_SOURCE_DIR}/Orthanc)
 set(ORTHANC_DISABLE_PATCH ON)  # No need for the "patch" command-line tool
@@ -92,8 +92,10 @@
   )
 
 add_definitions(
+  -DORTHANC_DEFAULT_DICOM_ENCODING=Encoding_Latin1
   -DORTHANC_ENABLE_LOCALE=0
   -DORTHANC_ENABLE_PUGIXML=0
+  -DORTHANC_ENABLE_SQLITE=1
   -DORTHANC_SQLITE_STANDALONE=1
   )
 
@@ -201,7 +203,7 @@
 
 add_executable(UnitTests
   ${CORE_SOURCES}
-  ${GTEST_SOURCES}
+  ${GOOGLE_TEST_SOURCES}
   ${JSONCPP_SOURCES}
   UnitTestsSources/UnitTestsMain.cpp
   )
--- a/Orthanc/Core/DicomFormat/DicomMap.cpp	Thu Aug 24 19:49:20 2017 +0200
+++ b/Orthanc/Core/DicomFormat/DicomMap.cpp	Tue Oct 10 12:53:46 2017 +0200
@@ -36,7 +36,9 @@
 
 #include <stdio.h>
 #include <memory>
+
 #include "../Endianness.h"
+#include "../Logging.h"
 #include "../OrthancException.h"
 
 
@@ -781,4 +783,193 @@
 
     return true;
   }
+
+
+  static std::string ValueAsString(const DicomMap& summary,
+                                   const DicomTag& tag)
+  {
+    const DicomValue& value = summary.GetValue(tag);
+    if (value.IsNull())
+    {
+      return "(null)";
+    }
+    else
+    {
+      return value.GetContent();
+    }
+  }
+
+
+  void DicomMap::LogMissingTagsForStore() const
+  {
+    std::string s, t;
+
+    if (HasTag(DICOM_TAG_PATIENT_ID))
+    {
+      if (t.size() > 0)
+        t += ", ";
+      t += "PatientID=" + ValueAsString(*this, DICOM_TAG_PATIENT_ID);
+    }
+    else
+    {
+      if (s.size() > 0)
+        s += ", ";
+      s += "PatientID";
+    }
+
+    if (HasTag(DICOM_TAG_STUDY_INSTANCE_UID))
+    {
+      if (t.size() > 0)
+        t += ", ";
+      t += "StudyInstanceUID=" + ValueAsString(*this, DICOM_TAG_STUDY_INSTANCE_UID);
+    }
+    else
+    {
+      if (s.size() > 0)
+        s += ", ";
+      s += "StudyInstanceUID";
+    }
+
+    if (HasTag(DICOM_TAG_SERIES_INSTANCE_UID))
+    {
+      if (t.size() > 0)
+        t += ", ";
+      t += "SeriesInstanceUID=" + ValueAsString(*this, DICOM_TAG_SERIES_INSTANCE_UID);
+    }
+    else
+    {
+      if (s.size() > 0)
+        s += ", ";
+      s += "SeriesInstanceUID";
+    }
+
+    if (HasTag(DICOM_TAG_SOP_INSTANCE_UID))
+    {
+      if (t.size() > 0)
+        t += ", ";
+      t += "SOPInstanceUID=" + ValueAsString(*this, DICOM_TAG_SOP_INSTANCE_UID);
+    }
+    else
+    {
+      if (s.size() > 0)
+        s += ", ";
+      s += "SOPInstanceUID";
+    }
+
+    if (t.size() == 0)
+    {
+      LOG(ERROR) << "Store has failed because all the required tags (" << s << ") are missing (is it a DICOMDIR file?)";
+    }
+    else
+    {
+      LOG(ERROR) << "Store has failed because required tags (" << s << ") are missing for the following instance: " << t;
+    }
+  }
+
+
+  bool DicomMap::CopyToString(std::string& result,
+                              const DicomTag& tag,
+                              bool allowBinary) const
+  {
+    const DicomValue* value = TestAndGetValue(tag);
+
+    if (value == NULL)
+    {
+      return false;
+    }
+    else
+    {
+      return value->CopyToString(result, allowBinary);
+    }
+  }
+    
+  bool DicomMap::ParseInteger32(int32_t& result,
+                                const DicomTag& tag) const
+  {
+    const DicomValue* value = TestAndGetValue(tag);
+
+    if (value == NULL)
+    {
+      return false;
+    }
+    else
+    {
+      return value->ParseInteger32(result);
+    }
+  }
+
+  bool DicomMap::ParseInteger64(int64_t& result,
+                                const DicomTag& tag) const
+  {
+    const DicomValue* value = TestAndGetValue(tag);
+
+    if (value == NULL)
+    {
+      return false;
+    }
+    else
+    {
+      return value->ParseInteger64(result);
+    }
+  }
+
+  bool DicomMap::ParseUnsignedInteger32(uint32_t& result,
+                                        const DicomTag& tag) const
+  {
+    const DicomValue* value = TestAndGetValue(tag);
+
+    if (value == NULL)
+    {
+      return false;
+    }
+    else
+    {
+      return value->ParseUnsignedInteger32(result);
+    }
+  }
+
+  bool DicomMap::ParseUnsignedInteger64(uint64_t& result,
+                                        const DicomTag& tag) const
+  {
+    const DicomValue* value = TestAndGetValue(tag);
+
+    if (value == NULL)
+    {
+      return false;
+    }
+    else
+    {
+      return value->ParseUnsignedInteger64(result);
+    }
+  }
+
+  bool DicomMap::ParseFloat(float& result,
+                            const DicomTag& tag) const
+  {
+    const DicomValue* value = TestAndGetValue(tag);
+
+    if (value == NULL)
+    {
+      return false;
+    }
+    else
+    {
+      return value->ParseFloat(result);
+    }
+  }
+
+  bool DicomMap::ParseDouble(double& result,
+                             const DicomTag& tag) const
+  {
+    const DicomValue* value = TestAndGetValue(tag);
+
+    if (value == NULL)
+    {
+      return false;
+    }
+    else
+    {
+      return value->ParseDouble(result);
+    }
+  }
 }
--- a/Orthanc/Core/DicomFormat/DicomMap.h	Thu Aug 24 19:49:20 2017 +0200
+++ b/Orthanc/Core/DicomFormat/DicomMap.h	Tue Oct 10 12:53:46 2017 +0200
@@ -181,5 +181,29 @@
     static bool ParseDicomMetaInformation(DicomMap& result,
                                           const char* dicom,
                                           size_t size);
+
+    void LogMissingTagsForStore() const;
+
+    bool CopyToString(std::string& result,
+                      const DicomTag& tag,
+                      bool allowBinary) const;
+    
+    bool ParseInteger32(int32_t& result,
+                        const DicomTag& tag) const;
+
+    bool ParseInteger64(int64_t& result,
+                        const DicomTag& tag) const;                                
+
+    bool ParseUnsignedInteger32(uint32_t& result,
+                                const DicomTag& tag) const;
+
+    bool ParseUnsignedInteger64(uint64_t& result,
+                                const DicomTag& tag) const;                                
+
+    bool ParseFloat(float& result,
+                    const DicomTag& tag) const;                                
+
+    bool ParseDouble(double& result,
+                     const DicomTag& tag) const;                                
   };
 }
--- a/Orthanc/Core/DicomFormat/DicomTag.h	Thu Aug 24 19:49:20 2017 +0200
+++ b/Orthanc/Core/DicomFormat/DicomTag.h	Tue Oct 10 12:53:46 2017 +0200
@@ -173,6 +173,17 @@
   static const DicomTag DICOM_TAG_ACQUISITION_DEVICE_PROCESSING_DESCRIPTION(0x0018, 0x1400);
   static const DicomTag DICOM_TAG_CONTRAST_BOLUS_AGENT(0x0018, 0x0010);
 
+  // Tags used within the Stone of Orthanc
+  static const DicomTag DICOM_TAG_FRAME_INCREMENT_POINTER(0x0028, 0x0009);
+  static const DicomTag DICOM_TAG_GRID_FRAME_OFFSET_VECTOR(0x3004, 0x000c);
+  static const DicomTag DICOM_TAG_PIXEL_SPACING(0x0028, 0x0030);
+  static const DicomTag DICOM_TAG_RESCALE_INTERCEPT(0x0028, 0x1052);
+  static const DicomTag DICOM_TAG_RESCALE_SLOPE(0x0028, 0x1053);
+  static const DicomTag DICOM_TAG_SLICE_THICKNESS(0x0018, 0x0050);
+  static const DicomTag DICOM_TAG_WINDOW_CENTER(0x0028, 0x1050);
+  static const DicomTag DICOM_TAG_WINDOW_WIDTH(0x0028, 0x1051);
+  static const DicomTag DICOM_TAG_DOSE_GRID_SCALING(0x3004, 0x000e);
+                                    
   // Counting patients, studies and series
   // https://www.medicalconnections.co.uk/kb/Counting_Studies_Series_and_Instances
   static const DicomTag DICOM_TAG_NUMBER_OF_PATIENT_RELATED_STUDIES(0x0020, 0x1200);  
--- a/Orthanc/Core/DicomFormat/DicomValue.cpp	Thu Aug 24 19:49:20 2017 +0200
+++ b/Orthanc/Core/DicomFormat/DicomValue.cpp	Tue Oct 10 12:53:46 2017 +0200
@@ -37,6 +37,8 @@
 #include "../OrthancException.h"
 #include "../Toolbox.h"
 
+#include <boost/lexical_cast.hpp>
+
 namespace Orthanc
 {
   DicomValue::DicomValue(const DicomValue& other) : 
@@ -91,4 +93,104 @@
   }
 #endif
 
+
+  template <typename T,
+            bool allowSigned>
+  static bool ParseValue(T& result,
+                         const DicomValue& source)
+  {
+    if (source.IsBinary() ||
+        source.IsNull())
+    {
+      return false;
+    }
+    
+    try
+    {
+      std::string value = Toolbox::StripSpaces(source.GetContent());
+      if (value.empty())
+      {
+        return false;
+      }
+
+      if (!allowSigned &&
+          value[0] == '-')
+      {
+        return false;
+      }
+      
+      result = boost::lexical_cast<T>(value);
+      return true;
+    }
+    catch (boost::bad_lexical_cast&)
+    {
+      return false;
+    }
+  }
+
+  bool DicomValue::ParseInteger32(int32_t& result) const
+  {
+    int64_t tmp;
+    if (ParseValue<int64_t, true>(tmp, *this))
+    {
+      result = static_cast<int32_t>(tmp);
+      return (tmp == static_cast<int64_t>(result));  // Check no overflow occurs
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+  bool DicomValue::ParseInteger64(int64_t& result) const
+  {
+    return ParseValue<int64_t, true>(result, *this);
+  }
+
+  bool DicomValue::ParseUnsignedInteger32(uint32_t& result) const
+  {
+    uint64_t tmp;
+    if (ParseValue<uint64_t, false>(tmp, *this))
+    {
+      result = static_cast<uint32_t>(tmp);
+      return (tmp == static_cast<uint64_t>(result));  // Check no overflow occurs
+    }
+    else
+    {
+      return false;
+    }
+  }
+
+  bool DicomValue::ParseUnsignedInteger64(uint64_t& result) const
+  {
+    return ParseValue<uint64_t, false>(result, *this);
+  }
+
+  bool DicomValue::ParseFloat(float& result) const
+  {
+    return ParseValue<float, true>(result, *this);
+  }
+
+  bool DicomValue::ParseDouble(double& result) const
+  {
+    return ParseValue<double, true>(result, *this);
+  }
+
+  bool DicomValue::CopyToString(std::string& result,
+                                bool allowBinary) const
+  {
+    if (IsNull())
+    {
+      return false;
+    }
+    else if (IsBinary() && !allowBinary)
+    {
+      return false;
+    }
+    else
+    {
+      result.assign(content_);
+      return true;
+    }
+  }    
 }
--- a/Orthanc/Core/DicomFormat/DicomValue.h	Thu Aug 24 19:49:20 2017 +0200
+++ b/Orthanc/Core/DicomFormat/DicomValue.h	Tue Oct 10 12:53:46 2017 +0200
@@ -33,6 +33,7 @@
 
 #pragma once
 
+#include <stdint.h>
 #include <string>
 #include <boost/noncopyable.hpp>
 
@@ -93,5 +94,20 @@
       FormatDataUriScheme(target, "application/octet-stream");
     }
 #endif
+
+    bool CopyToString(std::string& result,
+                      bool allowBinary) const;
+    
+    bool ParseInteger32(int32_t& result) const;
+
+    bool ParseInteger64(int64_t& result) const;                                
+
+    bool ParseUnsignedInteger32(uint32_t& result) const;
+
+    bool ParseUnsignedInteger64(uint64_t& result) const;                                
+
+    bool ParseFloat(float& result) const;                                
+
+    bool ParseDouble(double& result) const;                                
   };
 }
--- a/Orthanc/Core/Endianness.h	Thu Aug 24 19:49:20 2017 +0200
+++ b/Orthanc/Core/Endianness.h	Tue Oct 10 12:53:46 2017 +0200
@@ -35,10 +35,10 @@
 
 
 /********************************************************************
- ** LINUX ARCHITECTURES
+ ** LINUX-LIKE ARCHITECTURES
  ********************************************************************/
 
-#if defined(__linux__)
+#if defined(__linux__) || defined(__EMSCRIPTEN__)
 #  define ORTHANC_HAS_BUILTIN_BYTE_SWAP 1
 #  include <endian.h>
 #endif
--- a/Orthanc/Core/Enumerations.cpp	Thu Aug 24 19:49:20 2017 +0200
+++ b/Orthanc/Core/Enumerations.cpp	Tue Oct 10 12:53:46 2017 +0200
@@ -38,6 +38,7 @@
 #include "Toolbox.h"
 #include "Logging.h"
 
+#include <boost/thread/mutex.hpp>
 #include <string.h>
 #include <cassert>
 
@@ -752,12 +753,128 @@
       case PixelFormat_Float32:
         return "Grayscale (float 32bpp)";
 
+      case PixelFormat_Grayscale32:
+        return "Grayscale (unsigned 32bpp)";
+
+      case PixelFormat_RGB48:
+        return "RGB48";
+
       default:
         throw OrthancException(ErrorCode_ParameterOutOfRange);
     }
   }
 
 
+  const char* EnumerationToString(ModalityManufacturer manufacturer)
+  {
+    switch (manufacturer)
+    {
+      case ModalityManufacturer_Generic:
+        return "Generic";
+
+      case ModalityManufacturer_GenericNoWildcardInDates:
+        return "GenericNoWildcardInDates";
+
+      case ModalityManufacturer_GenericNoUniversalWildcard:
+        return "GenericNoUniversalWildcard";
+
+      case ModalityManufacturer_StoreScp:
+        return "StoreScp";
+      
+      case ModalityManufacturer_ClearCanvas:
+        return "ClearCanvas";
+      
+      case ModalityManufacturer_Dcm4Chee:
+        return "Dcm4Chee";
+      
+      case ModalityManufacturer_Vitrea:
+        return "Vitrea";
+      
+      default:
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+  }
+
+
+  const char* EnumerationToString(DicomRequestType type)
+  {
+    switch (type)
+    {
+      case DicomRequestType_Echo:
+        return "Echo";
+        break;
+
+      case DicomRequestType_Find:
+        return "Find";
+        break;
+
+      case DicomRequestType_Get:
+        return "Get";
+        break;
+
+      case DicomRequestType_Move:
+        return "Move";
+        break;
+
+      case DicomRequestType_Store:
+        return "Store";
+        break;
+
+      default: 
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+  }
+
+
+  const char* EnumerationToString(TransferSyntax syntax)
+  {
+    switch (syntax)
+    {
+      case TransferSyntax_Deflated:
+        return "Deflated";
+
+      case TransferSyntax_Jpeg:
+        return "JPEG";
+
+      case TransferSyntax_Jpeg2000:
+        return "JPEG2000";
+
+      case TransferSyntax_JpegLossless:
+        return "JPEG Lossless";
+
+      case TransferSyntax_Jpip:
+        return "JPIP";
+
+      case TransferSyntax_Mpeg2:
+        return "MPEG2";
+
+      case TransferSyntax_Rle:
+        return "RLE";
+
+      default: 
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+  }
+
+
+  const char* EnumerationToString(DicomVersion version)
+  {
+    switch (version)
+    {
+      case DicomVersion_2008:
+        return "2008";
+        break;
+
+      case DicomVersion_2017c:
+        return "2017c";
+        break;
+
+      default: 
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+  }
+
+
   Encoding StringToEncoding(const char* encoding)
   {
     std::string s(encoding);
@@ -1127,6 +1244,86 @@
   }
   
 
+  ModalityManufacturer StringToModalityManufacturer(const std::string& manufacturer)
+  {
+    ModalityManufacturer result;
+    bool obsolete = false;
+    
+    if (manufacturer == "Generic")
+    {
+      return ModalityManufacturer_Generic;
+    }
+    else if (manufacturer == "GenericNoWildcardInDates")
+    {
+      return ModalityManufacturer_GenericNoWildcardInDates;
+    }
+    else if (manufacturer == "GenericNoUniversalWildcard")
+    {
+      return ModalityManufacturer_GenericNoUniversalWildcard;
+    }
+    else if (manufacturer == "ClearCanvas")
+    {
+      return ModalityManufacturer_ClearCanvas;
+    }
+    else if (manufacturer == "StoreScp")
+    {
+      return ModalityManufacturer_StoreScp;
+    }
+    else if (manufacturer == "Dcm4Chee")
+    {
+      return ModalityManufacturer_Dcm4Chee;
+    }
+    else if (manufacturer == "Vitrea")
+    {
+      return ModalityManufacturer_Vitrea;
+    }
+    else if (manufacturer == "AgfaImpax" ||
+             manufacturer == "SyngoVia")
+    {
+      result = ModalityManufacturer_GenericNoWildcardInDates;
+      obsolete = true;
+    }
+    else if (manufacturer == "EFilm2" ||
+             manufacturer == "MedInria")
+    {
+      result = ModalityManufacturer_Generic;
+      obsolete = true;
+    }
+    else
+    {
+      throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+
+    if (obsolete)
+    {
+      LOG(WARNING) << "The \"" << manufacturer << "\" manufacturer is obsolete since "
+                   << "Orthanc 1.3.0. To guarantee compatibility with future Orthanc "
+                   << "releases, you should replace it by \""
+                   << EnumerationToString(result)
+                   << "\" in your configuration file.";
+    }
+
+    return result;
+  }
+
+
+  DicomVersion StringToDicomVersion(const std::string& version)
+  {
+    if (version == "2008")
+    {
+      return DicomVersion_2008;
+    }
+    else if (version == "2017c")
+    {
+      return DicomVersion_2017c;
+    }
+    else
+    {
+      throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+  }
+
+
   unsigned int GetBytesPerPixel(PixelFormat format)
   {
     switch (format)
@@ -1143,12 +1340,16 @@
 
       case PixelFormat_RGBA32:
       case PixelFormat_BGRA32:
+      case PixelFormat_Grayscale32:
         return 4;
 
       case PixelFormat_Float32:
         assert(sizeof(float) == 4);
         return 4;
 
+      case PixelFormat_RGB48:
+        return 6;
+
       default:
         throw OrthancException(ErrorCode_ParameterOutOfRange);
     }
@@ -1225,8 +1426,15 @@
     {
       encoding = Encoding_Japanese;
     }
-    else if (s == "GB18030")
+    else if (s == "GB18030" || s == "GBK")
     {
+      /**
+       * According to tumashu@163.com, "In China, many dicom file's
+       * 0008,0005 tag is set as "GBK", instead of "GB18030", GBK is a
+       * subset of GB18030, and which is used frequently in China,
+       * suggest support it."
+       * https://groups.google.com/d/msg/orthanc-users/WMM8LMbjpUc/02-1f_yFCgAJ
+       **/
       encoding = Encoding_Chinese;
     }
     /*
@@ -1475,5 +1683,28 @@
       default:
         throw OrthancException(ErrorCode_ParameterOutOfRange);
     }
+  }  
+
+
+  static boost::mutex  defaultEncodingMutex_;  // Should not be necessary
+  static Encoding      defaultEncoding_ = ORTHANC_DEFAULT_DICOM_ENCODING;
+  
+  Encoding GetDefaultDicomEncoding()
+  {
+    boost::mutex::scoped_lock lock(defaultEncodingMutex_);
+    return defaultEncoding_;
   }
+
+  void SetDefaultDicomEncoding(Encoding encoding)
+  {
+    std::string name = EnumerationToString(encoding);
+    
+    {
+      boost::mutex::scoped_lock lock(defaultEncodingMutex_);
+      defaultEncoding_ = encoding;
+    }
+
+    LOG(INFO) << "Default encoding for DICOM was changed to: " << name;
+  }
+
 }
--- a/Orthanc/Core/Enumerations.h	Thu Aug 24 19:49:20 2017 +0200
+++ b/Orthanc/Core/Enumerations.h	Tue Oct 10 12:53:46 2017 +0200
@@ -199,8 +199,21 @@
      **/
     PixelFormat_Float32 = 6,
 
-    // This is the memory layout for Cairo
-    PixelFormat_BGRA32 = 7
+    // This is the memory layout for Cairo (for internal use in Stone of Orthanc)
+    PixelFormat_BGRA32 = 7,
+
+    /**
+     * {summary}{Graylevel, unsigned 32bpp image.}
+     * {description}{The image is graylevel. Each pixel is unsigned and stored in 4 bytes.}
+     **/
+    PixelFormat_Grayscale32 = 8,
+    
+    /**
+     * {summary}{Color image in RGB48 format.}
+     * {description}{This format describes a color image. The pixels are stored in 6
+     * consecutive bytes. The memory layout is RGB.}
+     **/
+    PixelFormat_RGB48 = 9
   };
 
 
@@ -443,6 +456,81 @@
     ValueRepresentation_NotSupported               // Not supported by Orthanc, or tag not in dictionary
   };
 
+  enum DicomReplaceMode
+  {
+    DicomReplaceMode_InsertIfAbsent,
+    DicomReplaceMode_ThrowIfAbsent,
+    DicomReplaceMode_IgnoreIfAbsent
+  };
+
+  enum DicomToJsonFormat
+  {
+    DicomToJsonFormat_Full,
+    DicomToJsonFormat_Short,
+    DicomToJsonFormat_Human
+  };
+
+  enum DicomToJsonFlags
+  {
+    DicomToJsonFlags_IncludeBinary         = (1 << 0),
+    DicomToJsonFlags_IncludePrivateTags    = (1 << 1),
+    DicomToJsonFlags_IncludeUnknownTags    = (1 << 2),
+    DicomToJsonFlags_IncludePixelData      = (1 << 3),
+    DicomToJsonFlags_ConvertBinaryToAscii  = (1 << 4),
+    DicomToJsonFlags_ConvertBinaryToNull   = (1 << 5),
+
+    // Some predefined combinations
+    DicomToJsonFlags_None     = 0,
+    DicomToJsonFlags_Default  = (DicomToJsonFlags_IncludeBinary |
+                                 DicomToJsonFlags_IncludePixelData | 
+                                 DicomToJsonFlags_IncludePrivateTags | 
+                                 DicomToJsonFlags_IncludeUnknownTags | 
+                                 DicomToJsonFlags_ConvertBinaryToNull)
+  };
+  
+  enum DicomFromJsonFlags
+  {
+    DicomFromJsonFlags_DecodeDataUriScheme = (1 << 0),
+    DicomFromJsonFlags_GenerateIdentifiers = (1 << 1)
+  };
+  
+  enum DicomVersion
+  {
+    DicomVersion_2008,
+    DicomVersion_2017c
+  };
+
+  enum ModalityManufacturer
+  {
+    ModalityManufacturer_Generic,
+    ModalityManufacturer_GenericNoWildcardInDates,
+    ModalityManufacturer_GenericNoUniversalWildcard,
+    ModalityManufacturer_StoreScp,
+    ModalityManufacturer_ClearCanvas,
+    ModalityManufacturer_Dcm4Chee,
+    ModalityManufacturer_Vitrea
+  };
+
+  enum DicomRequestType
+  {
+    DicomRequestType_Echo,
+    DicomRequestType_Find,
+    DicomRequestType_Get,
+    DicomRequestType_Move,
+    DicomRequestType_Store
+  };
+
+  enum TransferSyntax
+  {
+    TransferSyntax_Deflated,
+    TransferSyntax_Jpeg,
+    TransferSyntax_Jpeg2000,
+    TransferSyntax_JpegLossless,
+    TransferSyntax_Jpip,
+    TransferSyntax_Mpeg2,
+    TransferSyntax_Rle
+  };
+
 
   /**
    * WARNING: Do not change the explicit values in the enumerations
@@ -513,6 +601,14 @@
 
   const char* EnumerationToString(PixelFormat format);
 
+  const char* EnumerationToString(ModalityManufacturer manufacturer);
+
+  const char* EnumerationToString(DicomRequestType type);
+
+  const char* EnumerationToString(TransferSyntax syntax);
+
+  const char* EnumerationToString(DicomVersion version);
+
   Encoding StringToEncoding(const char* encoding);
 
   ResourceType StringToResourceType(const char* type);
@@ -525,6 +621,10 @@
                                                   bool throwIfUnsupported);
 
   PhotometricInterpretation StringToPhotometricInterpretation(const char* value);
+
+  ModalityManufacturer StringToModalityManufacturer(const std::string& manufacturer);
+
+  DicomVersion StringToDicomVersion(const std::string& version);
   
   unsigned int GetBytesPerPixel(PixelFormat format);
 
@@ -544,4 +644,8 @@
   bool IsUserContentType(FileContentType type);
 
   bool IsBinaryValueRepresentation(ValueRepresentation vr);
+  
+  Encoding GetDefaultDicomEncoding();
+
+  void SetDefaultDicomEncoding(Encoding encoding);
 }
--- a/Orthanc/Core/FileStorage/FilesystemStorage.h	Thu Aug 24 19:49:20 2017 +0200
+++ b/Orthanc/Core/FileStorage/FilesystemStorage.h	Tue Oct 10 12:53:46 2017 +0200
@@ -33,6 +33,14 @@
 
 #pragma once
 
+#if !defined(ORTHANC_SANDBOXED)
+#  error The macro ORTHANC_SANDBOXED must be defined
+#endif
+
+#if ORTHANC_SANDBOXED == 1
+#  error The class FilesystemStorage cannot be used in sandboxed environments
+#endif
+
 #include "IStorageArea.h"
 
 #include <stdint.h>
--- a/Orthanc/Core/Images/ImageAccessor.cpp	Thu Aug 24 19:49:20 2017 +0200
+++ b/Orthanc/Core/Images/ImageAccessor.cpp	Tue Oct 10 12:53:46 2017 +0200
@@ -218,6 +218,10 @@
         ToMatlabStringInternal<uint16_t>(buffer, *this);
         break;
 
+      case PixelFormat_Grayscale32:
+        ToMatlabStringInternal<uint32_t>(buffer, *this);
+        break;
+
       case PixelFormat_SignedGrayscale16:
         ToMatlabStringInternal<int16_t>(buffer, *this);
         break;
--- a/Orthanc/Core/Images/ImageProcessing.cpp	Thu Aug 24 19:49:20 2017 +0200
+++ b/Orthanc/Core/Images/ImageProcessing.cpp	Tue Oct 10 12:53:46 2017 +0200
@@ -413,6 +413,13 @@
     }
 
     if (target.GetFormat() == PixelFormat_Float32 &&
+        source.GetFormat() == PixelFormat_Grayscale32)
+    {
+      ConvertGrayscaleToFloat<uint32_t>(target, source);
+      return;
+    }
+
+    if (target.GetFormat() == PixelFormat_Float32 &&
         source.GetFormat() == PixelFormat_SignedGrayscale16)
     {
       ConvertGrayscaleToFloat<int16_t>(target, source);
@@ -561,6 +568,26 @@
       return;
     }
 
+    if (target.GetFormat() == PixelFormat_RGB24 &&
+        source.GetFormat() == PixelFormat_RGB48)
+    {
+      for (unsigned int y = 0; y < source.GetHeight(); y++)
+      {
+        const uint16_t* p = reinterpret_cast<const uint16_t*>(source.GetConstRow(y));
+        uint8_t* q = reinterpret_cast<uint8_t*>(target.GetRow(y));
+        for (unsigned int x = 0; x < source.GetWidth(); x++)
+        {
+          q[0] = p[0] >> 8;
+          q[1] = p[1] >> 8;
+          q[2] = p[2] >> 8;
+          p += 3;
+          q += 3;
+        }
+      }
+
+      return;
+    }
+
     throw OrthancException(ErrorCode_NotImplemented);
   }
 
@@ -579,6 +606,10 @@
         SetInternal<uint16_t>(image, value);
         return;
 
+      case PixelFormat_Grayscale32:
+        SetInternal<uint32_t>(image, value);
+        return;
+
       case PixelFormat_SignedGrayscale16:
         SetInternal<int16_t>(image, value);
         return;
@@ -664,9 +695,9 @@
   }
 
 
-  void ImageProcessing::GetMinMaxValue(int64_t& minValue,
-                                       int64_t& maxValue,
-                                       const ImageAccessor& image)
+  void ImageProcessing::GetMinMaxIntegerValue(int64_t& minValue,
+                                              int64_t& maxValue,
+                                              const ImageAccessor& image)
   {
     switch (image.GetFormat())
     {
@@ -688,6 +719,15 @@
         break;
       }
 
+      case PixelFormat_Grayscale32:
+      {
+        uint32_t a, b;
+        GetMinMaxValueInternal<uint32_t>(a, b, image);
+        minValue = a;
+        maxValue = b;
+        break;
+      }
+
       case PixelFormat_SignedGrayscale16:
       {
         int16_t a, b;
@@ -703,6 +743,28 @@
   }
 
 
+  void ImageProcessing::GetMinMaxFloatValue(float& minValue,
+                                            float& maxValue,
+                                            const ImageAccessor& image)
+  {
+    switch (image.GetFormat())
+    {
+      case PixelFormat_Float32:
+      {
+        assert(sizeof(float) == 32);
+        float a, b;
+        GetMinMaxValueInternal<float>(a, b, image);
+        minValue = a;
+        maxValue = b;
+        break;
+      }
+
+      default:
+        throw OrthancException(ErrorCode_NotImplemented);
+    }
+  }
+
+
 
   void ImageProcessing::AddConstant(ImageAccessor& image,
                                     int64_t value)
--- a/Orthanc/Core/Images/ImageProcessing.h	Thu Aug 24 19:49:20 2017 +0200
+++ b/Orthanc/Core/Images/ImageProcessing.h	Tue Oct 10 12:53:46 2017 +0200
@@ -60,9 +60,13 @@
     static void ShiftRight(ImageAccessor& target,
                            unsigned int shift);
 
-    static void GetMinMaxValue(int64_t& minValue,
-                               int64_t& maxValue,
-                               const ImageAccessor& image);
+    static void GetMinMaxIntegerValue(int64_t& minValue,
+                                      int64_t& maxValue,
+                                      const ImageAccessor& image);
+
+    static void GetMinMaxFloatValue(float& minValue,
+                                    float& maxValue,
+                                    const ImageAccessor& image);
 
     static void AddConstant(ImageAccessor& image,
                             int64_t value);
--- a/Orthanc/Core/PrecompiledHeaders.h	Thu Aug 24 19:49:20 2017 +0200
+++ b/Orthanc/Core/PrecompiledHeaders.h	Tue Oct 10 12:53:46 2017 +0200
@@ -50,7 +50,7 @@
 #include <json/value.h>
 
 #if ORTHANC_ENABLE_PUGIXML == 1
-#include <pugixml.hpp>
+#  include <pugixml.hpp>
 #endif
 
 #include "Enumerations.h"
@@ -58,4 +58,21 @@
 #include "OrthancException.h"
 #include "Toolbox.h"
 
+#if ORTHANC_ENABLE_DCMTK == 1
+#  include "DicomParsing/ParsedDicomFile.h"
+
+// Headers from DCMTK used in Orthanc headers 
+#  include <dcmtk/dcmdata/dcdatset.h>
+#  include <dcmtk/dcmdata/dcfilefo.h>
+#  include <dcmtk/dcmdata/dcmetinf.h>
+#  include <dcmtk/dcmdata/dcpixseq.h>
 #endif
+
+#if ORTHANC_ENABLE_DCMTK_NETWORKING == 1
+#  include "DicomNetworking/DicomServer.h"
+
+// Headers from DCMTK used in Orthanc headers 
+#  include <dcmtk/dcmnet/dimse.h>
+#endif
+
+#endif
--- a/Orthanc/Core/SQLite/OrthancSQLiteException.h	Thu Aug 24 19:49:20 2017 +0200
+++ b/Orthanc/Core/SQLite/OrthancSQLiteException.h	Tue Oct 10 12:53:46 2017 +0200
@@ -38,6 +38,11 @@
 #pragma once
 
 
+#if ORTHANC_ENABLE_SQLITE != 1
+#  error Macro ORTHANC_ENABLE_SQLITE must be set to 1 to use SQLite
+#endif
+
+
 #if ORTHANC_SQLITE_STANDALONE == 1
 #include <stdexcept>
 
--- a/Orthanc/Core/SQLite/Statement.h	Thu Aug 24 19:49:20 2017 +0200
+++ b/Orthanc/Core/SQLite/Statement.h	Tue Oct 10 12:53:46 2017 +0200
@@ -46,7 +46,7 @@
 #include <stdint.h>
 
 #if ORTHANC_BUILD_UNIT_TESTS == 1
-#include <gtest/gtest_prod.h>
+#  include <gtest/gtest_prod.h>
 #endif
 
 
--- a/Orthanc/Core/Toolbox.cpp	Thu Aug 24 19:49:20 2017 +0200
+++ b/Orthanc/Core/Toolbox.cpp	Tue Oct 10 12:53:46 2017 +0200
@@ -397,6 +397,7 @@
 #endif
 
 
+#if ORTHANC_ENABLE_LOCALE == 1
   static const char* GetBoostLocaleEncoding(const Encoding sourceEncoding)
   {
     switch (sourceEncoding)
@@ -463,6 +464,7 @@
         throw OrthancException(ErrorCode_NotImplemented);
     }
   }
+#endif
 
 
 #if ORTHANC_ENABLE_LOCALE == 1
--- a/Orthanc/Plugins/Samples/GdcmDecoder/GdcmImageDecoder.cpp	Thu Aug 24 19:49:20 2017 +0200
+++ b/Orthanc/Plugins/Samples/GdcmDecoder/GdcmImageDecoder.cpp	Tue Oct 10 12:53:46 2017 +0200
@@ -32,6 +32,17 @@
 #include <boost/iostreams/device/array.hpp>
 
 
+// This is for compatibility with Orthanc SDK <= 1.3.0
+#if !defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE)
+#define ORTHANC_PLUGINS_VERSION_IS_ABOVE(major, minor, revision) \
+  (ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER > major ||               \
+   (ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER == major &&             \
+    (ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER > minor ||             \
+     (ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER == minor &&           \
+      ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER >= revision))))
+#endif
+
+
 namespace OrthancPlugins
 {
   struct GdcmImageDecoder::PImpl
@@ -199,6 +210,13 @@
         case gdcm::PixelFormat::UINT8:
           return OrthancPluginPixelFormat_RGB24;
 
+        case gdcm::PixelFormat::UINT16:
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 3, 1)
+          return OrthancPluginPixelFormat_RGB48;
+#else
+          throw std::runtime_error("RGB48 pixel format is only supported by Orthanc >= 1.3.1");
+#endif
+          
         default:
           break;
       }      
--- a/Orthanc/Plugins/Samples/GdcmDecoder/GdcmImageDecoder.h	Thu Aug 24 19:49:20 2017 +0200
+++ b/Orthanc/Plugins/Samples/GdcmDecoder/GdcmImageDecoder.h	Tue Oct 10 12:53:46 2017 +0200
@@ -26,6 +26,18 @@
 #include <boost/noncopyable.hpp>
 #include <boost/shared_ptr.hpp>
 
+
+// This is for compatibility with Orthanc SDK <= 1.3.0
+#if !defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE)
+#define ORTHANC_PLUGINS_VERSION_IS_ABOVE(major, minor, revision) \
+  (ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER > major ||               \
+   (ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER == major &&             \
+    (ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER > minor ||             \
+     (ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER == minor &&           \
+      ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER >= revision))))
+#endif
+
+
 namespace OrthancPlugins
 {
   class GdcmImageDecoder : public boost::noncopyable
--- a/Orthanc/Resources/CMake/Compiler.cmake	Thu Aug 24 19:49:20 2017 +0200
+++ b/Orthanc/Resources/CMake/Compiler.cmake	Tue Oct 10 12:53:46 2017 +0200
@@ -179,6 +179,9 @@
     message(FATAL_ERROR "Please install the uuid-dev package")
   endif()
 
+elseif (CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
+  message("Building using Emscripten (for WebAssembly or asm.js targets)")
+
 else()
   message(FATAL_ERROR "Support your platform here")
 endif()
--- a/Orthanc/Resources/CMake/GoogleTestConfiguration.cmake	Thu Aug 24 19:49:20 2017 +0200
+++ b/Orthanc/Resources/CMake/GoogleTestConfiguration.cmake	Tue Oct 10 12:53:46 2017 +0200
@@ -1,5 +1,5 @@
-if (USE_GTEST_DEBIAN_SOURCE_PACKAGE)
-  find_path(GTEST_DEBIAN_SOURCES_DIR
+if (USE_GOOGLE_TEST_DEBIAN_PACKAGE)
+  find_path(GOOGLE_TEST_DEBIAN_SOURCES_DIR
     NAMES src/gtest-all.cc
     PATHS
     /usr/src/gtest
@@ -7,37 +7,40 @@
     PATH_SUFFIXES src
     )
 
-  find_path(GTEST_DEBIAN_INCLUDE_DIR
+  find_path(GOOGLE_TEST_DEBIAN_INCLUDE_DIR
     NAMES gtest.h
     PATHS
     /usr/include/gtest
     )
 
-  message("Path to the Debian Google Test sources: ${GTEST_DEBIAN_SOURCES_DIR}")
-  message("Path to the Debian Google Test includes: ${GTEST_DEBIAN_INCLUDE_DIR}")
+  message("Path to the Debian Google Test sources: ${GOOGLE_TEST_DEBIAN_SOURCES_DIR}")
+  message("Path to the Debian Google Test includes: ${GOOGLE_TEST_DEBIAN_INCLUDE_DIR}")
 
-  set(GTEST_SOURCES ${GTEST_DEBIAN_SOURCES_DIR}/src/gtest-all.cc)
-  include_directories(${GTEST_DEBIAN_SOURCES_DIR})
+  set(GOOGLE_TEST_SOURCES
+    ${GOOGLE_TEST_DEBIAN_SOURCES_DIR}/src/gtest-all.cc
+    )
 
-  if (NOT EXISTS ${GTEST_SOURCES} OR
-      NOT EXISTS ${GTEST_DEBIAN_INCLUDE_DIR}/gtest.h)
+  include_directories(${GOOGLE_TEST_DEBIAN_SOURCES_DIR})
+
+  if (NOT EXISTS ${GOOGLE_TEST_SOURCES} OR
+      NOT EXISTS ${GOOGLE_TEST_DEBIAN_INCLUDE_DIR}/gtest.h)
     message(FATAL_ERROR "Please install the libgtest-dev package")
   endif()
 
 elseif (STATIC_BUILD OR NOT USE_SYSTEM_GOOGLE_TEST)
-  set(GTEST_SOURCES_DIR ${CMAKE_BINARY_DIR}/gtest-1.7.0)
-  set(GTEST_URL "http://www.orthanc-server.com/downloads/third-party/gtest-1.7.0.zip")
-  set(GTEST_MD5 "2d6ec8ccdf5c46b05ba54a9fd1d130d7")
+  set(GOOGLE_TEST_SOURCES_DIR ${CMAKE_BINARY_DIR}/gtest-1.7.0)
+  set(GOOGLE_TEST_URL "http://www.orthanc-server.com/downloads/third-party/gtest-1.7.0.zip")
+  set(GOOGLE_TEST_MD5 "2d6ec8ccdf5c46b05ba54a9fd1d130d7")
 
-  DownloadPackage(${GTEST_MD5} ${GTEST_URL} "${GTEST_SOURCES_DIR}")
+  DownloadPackage(${GOOGLE_TEST_MD5} ${GOOGLE_TEST_URL} "${GOOGLE_TEST_SOURCES_DIR}")
 
   include_directories(
-    ${GTEST_SOURCES_DIR}/include
-    ${GTEST_SOURCES_DIR}
+    ${GOOGLE_TEST_SOURCES_DIR}/include
+    ${GOOGLE_TEST_SOURCES_DIR}
     )
 
-  set(GTEST_SOURCES
-    ${GTEST_SOURCES_DIR}/src/gtest-all.cc
+  set(GOOGLE_TEST_SOURCES
+    ${GOOGLE_TEST_SOURCES_DIR}/src/gtest-all.cc
     )
 
   # https://code.google.com/p/googletest/issues/detail?id=412
@@ -45,14 +48,16 @@
     add_definitions(/D _VARIADIC_MAX=10)
   endif()
 
-  source_group(ThirdParty\\GoogleTest REGULAR_EXPRESSION ${GTEST_SOURCES_DIR}/.*)
+  source_group(ThirdParty\\GoogleTest REGULAR_EXPRESSION ${GOOGLE_TEST_SOURCES_DIR}/.*)
 
 else()
   include(FindGTest)
-  if (NOT GTEST_FOUND)
+  if (NOT GOOGLE_TEST_FOUND)
     message(FATAL_ERROR "Unable to find GoogleTest")
   endif()
 
-  include_directories(${GTEST_INCLUDE_DIRS})
-  link_libraries(${GTEST_LIBRARIES})
+  include_directories(${GOOGLE_TEST_INCLUDE_DIRS})
+
+  # The variable GOOGLE_TEST_LIBRARIES contains the shared library of
+  # Google Test
 endif()
--- a/Plugin/DecodedImageAdapter.cpp	Thu Aug 24 19:49:20 2017 +0200
+++ b/Plugin/DecodedImageAdapter.cpp	Tue Oct 10 12:53:46 2017 +0200
@@ -206,7 +206,7 @@
       case PixelFormat_SignedGrayscale16:
       {
         int64_t a, b;
-        Orthanc::ImageProcessing::GetMinMaxValue(a, b, accessor);
+        Orthanc::ImageProcessing::GetMinMaxIntegerValue(a, b, accessor);
         result["minPixelValue"] = (a < 0 ? static_cast<int32_t>(a) : 0);
         result["maxPixelValue"] = (b > 0 ? static_cast<int32_t>(b) : 1);
         result["color"] = false;
@@ -414,7 +414,7 @@
       converted = buffer->GetAccessor();
 
       int64_t a, b;
-      Orthanc::ImageProcessing::GetMinMaxValue(a, b, accessor);
+      Orthanc::ImageProcessing::GetMinMaxIntegerValue(a, b, accessor);
       result["Orthanc"]["StretchLow"] = static_cast<int32_t>(a);
       result["Orthanc"]["StretchHigh"] = static_cast<int32_t>(b);
 
--- a/Plugin/ViewerToolbox.cpp	Thu Aug 24 19:49:20 2017 +0200
+++ b/Plugin/ViewerToolbox.cpp	Tue Oct 10 12:53:46 2017 +0200
@@ -24,6 +24,9 @@
 #include "../Orthanc/Core/OrthancException.h"
 #include "../Orthanc/Core/Toolbox.h"
 
+// Gain access to ORTHANC_PLUGINS_VERSION_IS_ABOVE if Orthanc SDK <= 1.3.0
+#include "../Orthanc/Plugins/Samples/GdcmDecoder/GdcmImageDecoder.h"
+
 #include <json/reader.h>
 #include <stdexcept>
 #include <boost/lexical_cast.hpp>
@@ -283,6 +286,11 @@
       case Orthanc::PixelFormat_RGB24:
         return OrthancPluginPixelFormat_RGB24;
 
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 3, 1)
+      case Orthanc::PixelFormat_RGB48:
+        return OrthancPluginPixelFormat_RGB48;
+#endif
+
       case Orthanc::PixelFormat_RGBA32:
         return OrthancPluginPixelFormat_RGBA32;
 
@@ -308,6 +316,11 @@
       case OrthancPluginPixelFormat_RGB24:
         return Orthanc::PixelFormat_RGB24;
 
+#if ORTHANC_PLUGINS_VERSION_IS_ABOVE(1, 3, 1)
+      case OrthancPluginPixelFormat_RGB48:
+        return Orthanc::PixelFormat_RGB48;
+#endif
+
       case OrthancPluginPixelFormat_RGBA32:
         return Orthanc::PixelFormat_RGBA32;