changeset 3915:7e33516965f8 transcoding

merging sample GDCM decoder and Orthanc Web viewer
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 11 May 2020 15:13:16 +0200
parents e14b5a7a0f75
children 0e3849268a55
files Plugins/Samples/Common/OrthancPluginCppWrapper.cpp Plugins/Samples/Common/OrthancPluginCppWrapper.h Plugins/Samples/GdcmDecoder/CMakeLists.txt Plugins/Samples/GdcmDecoder/GdcmConfiguration.cmake Plugins/Samples/GdcmDecoder/GdcmDecoderCache.cpp Plugins/Samples/GdcmDecoder/GdcmDecoderCache.h Plugins/Samples/GdcmDecoder/GdcmImageDecoder.cpp Plugins/Samples/GdcmDecoder/GdcmImageDecoder.h Plugins/Samples/GdcmDecoder/OrthancImageWrapper.cpp Plugins/Samples/GdcmDecoder/OrthancImageWrapper.h Plugins/Samples/GdcmDecoder/Plugin.cpp
diffstat 11 files changed, 405 insertions(+), 241 deletions(-) [+]
line wrap: on
line diff
--- a/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp	Mon May 11 12:12:23 2020 +0200
+++ b/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp	Mon May 11 15:13:16 2020 +0200
@@ -1045,7 +1045,7 @@
   }
 
 
-  const void* OrthancImage::GetBuffer() const
+  void* OrthancImage::GetBuffer() const
   {
     CheckImageAvailable();
     return OrthancPluginGetImageBuffer(GetGlobalContext(), image_);
@@ -1094,6 +1094,14 @@
   }
 
 
+  OrthancPluginImage* OrthancImage::Release()
+  {
+    CheckImageAvailable();
+    OrthancPluginImage* tmp = image_;
+    image_ = NULL;
+    return tmp;
+  }
+
 
 #if HAS_ORTHANC_PLUGIN_FIND_MATCHER == 1
   FindMatcher::FindMatcher(const OrthancPluginWorklistQuery* worklist) :
--- a/Plugins/Samples/Common/OrthancPluginCppWrapper.h	Mon May 11 12:12:23 2020 +0200
+++ b/Plugins/Samples/Common/OrthancPluginCppWrapper.h	Mon May 11 15:13:16 2020 +0200
@@ -412,7 +412,7 @@
 
     unsigned int GetPitch() const;
     
-    const void* GetBuffer() const;
+    void* GetBuffer() const;
 
     const OrthancPluginImage* GetObject() const
     {
@@ -428,6 +428,10 @@
 
     void AnswerJpegImage(OrthancPluginRestOutput* output,
                          uint8_t quality) const;
+    
+    void* GetWriteableBuffer();
+
+    OrthancPluginImage* Release();
   };
 
 
--- a/Plugins/Samples/GdcmDecoder/CMakeLists.txt	Mon May 11 12:12:23 2020 +0200
+++ b/Plugins/Samples/GdcmDecoder/CMakeLists.txt	Mon May 11 15:13:16 2020 +0200
@@ -1,32 +1,83 @@
+# Orthanc - A Lightweight, RESTful DICOM Store
+# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+# Department, University Hospital of Liege, Belgium
+# Copyright (C) 2017-2020 Osimis S.A., Belgium
+#
+# This program is free software: you can redistribute it and/or
+# modify it under the terms of the GNU Affero General Public License
+# as published by the Free Software Foundation, either version 3 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Affero General Public License for more details.
+# 
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
 cmake_minimum_required(VERSION 2.8)
 
 project(GdcmDecoder)
 
-SET(GDCM_DECODER_VERSION "0.0" CACHE STRING "Version of the plugin")
-SET(STATIC_BUILD OFF CACHE BOOL "Static build of the third-party libraries (necessary for Windows)")
-SET(ALLOW_DOWNLOADS OFF CACHE BOOL "Allow CMake to download packages")
-SET(USE_SYSTEM_BOOST ON CACHE BOOL "Use the system version of boost")
+SET(PLUGIN_VERSION "0.0" CACHE STRING "Version of the plugin")
+
+
+# Parameters of the build
+set(STATIC_BUILD OFF CACHE BOOL "Static build of the third-party libraries (necessary for Windows)")
+set(STANDALONE_BUILD ON CACHE BOOL "Standalone build (all the resources are embedded, necessary for releases)")
+set(ALLOW_DOWNLOADS OFF CACHE BOOL "Allow CMake to download packages")
 
-set(SAMPLES_ROOT ${CMAKE_SOURCE_DIR}/..)
-include(${SAMPLES_ROOT}/Common/OrthancPlugins.cmake)
-include(${ORTHANC_ROOT}/Resources/CMake/BoostConfiguration.cmake)
+# Advanced parameters to fine-tune linking against system libraries
+set(USE_SYSTEM_GDCM ON CACHE BOOL "Use the system version of Grassroot DICOM (GDCM)")
+set(USE_SYSTEM_ORTHANC_SDK ON CACHE BOOL "Use the system version of the Orthanc plugin SDK")
+
+# Setup the Orthanc framework
+set(ORTHANC_ROOT ${CMAKE_SOURCE_DIR}/../../..)
+
+set(ORTHANC_FRAMEWORK_PLUGIN ON)
+include(${ORTHANC_ROOT}/Resources/CMake/OrthancFrameworkParameters.cmake)
+
+set(ENABLE_LOCALE OFF CACHE INTERNAL "")      # Disable support for locales (notably in Boost)
+set(ENABLE_MODULE_IMAGES OFF CACHE INTERNAL "")
+set(ENABLE_MODULE_JOBS OFF CACHE INTERNAL "")
 
-find_package(GDCM REQUIRED)
-if (GDCM_FOUND)
-  include(${GDCM_USE_FILE})
-  set(GDCM_LIBRARIES gdcmCommon gdcmMSFF)
-else(GDCM_FOUND)
-  message(FATAL_ERROR "Cannot find GDCM, did you set GDCM_DIR?")
-endif(GDCM_FOUND)
+include(${ORTHANC_ROOT}/Resources/CMake/OrthancFrameworkConfiguration.cmake)
+
+include(GdcmConfiguration.cmake)
+
 
-add_definitions(-DGDCM_DECODER_VERSION="${GDCM_DECODER_VERSION}")
+# Check that the Orthanc SDK headers are available
+if (STATIC_BUILD OR NOT USE_SYSTEM_ORTHANC_SDK)
+  #include_directories(${CMAKE_SOURCE_DIR}/Resources/Orthanc/Sdk-0.9.5)
+  include_directories(${CMAKE_SOURCE_DIR}/../../Include) # TODO => SYNC 0.9.5
+else ()
+  CHECK_INCLUDE_FILE_CXX(orthanc/OrthancCPlugin.h HAVE_ORTHANC_H)
+  if (NOT HAVE_ORTHANC_H)
+    message(FATAL_ERROR "Please install the headers of the Orthanc plugins SDK")
+  endif()
+endif()
+
+
+include_directories(${ORTHANC_ROOT})
+
+add_definitions(
+  -DPLUGIN_VERSION="${PLUGIN_VERSION}"
+  -DHAS_ORTHANC_EXCEPTION=1
+  -DORTHANC_ENABLE_LOGGING_PLUGIN=1
+  )
 
 add_library(GdcmDecoder SHARED
-  ${BOOST_SOURCES}
   GdcmDecoderCache.cpp
   GdcmImageDecoder.cpp
-  OrthancImageWrapper.cpp
   Plugin.cpp
+  ${CMAKE_SOURCE_DIR}/../Common/OrthancPluginCppWrapper.cpp
+  ${ORTHANC_CORE_SOURCES}
   )
 
 target_link_libraries(GdcmDecoder ${GDCM_LIBRARIES})
+
+if (STATIC_BUILD OR NOT USE_SYSTEM_GDCM)
+  add_dependencies(GdcmDecoder GDCM)
+endif()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/Samples/GdcmDecoder/GdcmConfiguration.cmake	Mon May 11 15:13:16 2020 +0200
@@ -0,0 +1,141 @@
+# Orthanc - A Lightweight, RESTful DICOM Store
+# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+# Department, University Hospital of Liege, Belgium
+# Copyright (C) 2017-2020 Osimis S.A., Belgium
+#
+# This program is free software: you can redistribute it and/or
+# modify it under the terms of the GNU Affero General Public License
+# as published by the Free Software Foundation, either version 3 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Affero General Public License for more details.
+# 
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+if (STATIC_BUILD OR NOT USE_SYSTEM_GDCM)
+  if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR
+      ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" OR
+      ${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD")
+    # If using gcc, build GDCM with the "-fPIC" argument to allow its
+    # embedding into the shared library containing the Orthanc plugin
+    set(AdditionalCFlags "-fPIC")
+    set(AdditionalCxxFlags ${AdditionalCFlags})
+  elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Windows" AND
+      CMAKE_COMPILER_IS_GNUCXX)
+    # Prevents error: "jump to label ‘err’ crosses initialization" of some variable
+    # within "Source/Common/gdcmCAPICryptographicMessageSyntax.cxx" if using MinGW
+    set(AdditionalCxxFlags "-fpermissive")
+  elseif (${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
+    # This definition is necessary to compile
+    # "Source/MediaStorageAndFileFormat/gdcmFileStreamer.cxx"
+    set(AdditionalCFlags "-Doff64_t=off_t") 
+    set(AdditionalCxxFlags ${AdditionalCFlags})
+  endif()
+  
+  set(Flags
+    "-DCMAKE_C_FLAGS=${CMAKE_C_FLAGS} ${AdditionalCFlags}"
+    "-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} ${AdditionalCxxFlags}"
+    -DCMAKE_C_FLAGS_DEBUG=${CMAKE_C_FLAGS_DEBUG}
+    -DCMAKE_CXX_FLAGS_DEBUG=${CMAKE_CXX_FLAGS_DEBUG}
+    -DCMAKE_C_FLAGS_RELEASE=${CMAKE_C_FLAGS_RELEASE}
+    -DCMAKE_CXX_FLAGS_RELEASE=${CMAKE_CXX_FLAGS_RELEASE}
+    -DCMAKE_C_FLAGS_MINSIZEREL=${CMAKE_C_FLAGS_MINSIZEREL}
+    -DCMAKE_CXX_FLAGS_MINSIZEREL=${CMAKE_CXX_FLAGS_MINSIZEREL} 
+    -DCMAKE_C_FLAGS_RELWITHDEBINFO=${CMAKE_C_FLAGS_RELWITHDEBINFO} 
+    -DCMAKE_CXX_FLAGS_RELWITHDEBINFO=${CMAKE_CXX_FLAGS_RELWITHDEBINFO}
+    )
+
+  if (CMAKE_TOOLCHAIN_FILE)
+    # Take absolute path to the toolchain
+    get_filename_component(TMP ${CMAKE_TOOLCHAIN_FILE} REALPATH BASE ${CMAKE_SOURCE_DIR})
+    list(APPEND Flags -DCMAKE_TOOLCHAIN_FILE=${TMP})
+  endif()
+
+  # Don't build manpages (since gdcm 2.8.4)
+  list(APPEND Flags -DGDCM_BUILD_DOCBOOK_MANPAGES=OFF)
+
+  if ("${CMAKE_SYSTEM_VERSION}" STREQUAL "LinuxStandardBase")
+    # Trick to disable the compilation of socket++ by gdcm, which is
+    # incompatible with LSB, but fortunately only required for DICOM
+    # Networking
+    list(APPEND Flags -DGDCM_USE_SYSTEM_SOCKETXX=ON)
+
+    # Detect the number of CPU cores to run "make" with as much
+    # parallelism as possible
+    include(ProcessorCount)
+    ProcessorCount(N)
+    if (NOT N EQUAL 0)
+      set(MAKE_PARALLEL -j${N})
+    endif()
+      
+    # For Linux Standard Base, avoid building incompatible target gdcmMEXD (*)
+    set(BUILD_COMMAND BUILD_COMMAND
+      ${CMAKE_MAKE_PROGRAM} ${MAKE_PARALLEL}
+      gdcmMSFF gdcmcharls gdcmDICT gdcmDSED gdcmIOD gdcmjpeg8
+      gdcmjpeg12 gdcmjpeg16 gdcmopenjp2 gdcmzlib gdcmCommon gdcmexpat)
+  endif()
+
+  include(ExternalProject)
+  externalproject_add(GDCM
+    URL "http://orthanc.osimis.io/ThirdPartyDownloads/gdcm-3.0.4.tar.gz"
+    URL_MD5 "f12dbded708356d5fa0b5ed37ccdb66e"
+    TIMEOUT 60
+    CMAKE_ARGS -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} ${Flags}
+    ${BUILD_COMMAND}    # Customize "make", only for Linux Standard Base (*)
+    INSTALL_COMMAND ""  # Skip the install step
+    )
+
+  if(MSVC)
+    set(Suffix ".lib")
+    set(Prefix "")
+  else()
+    set(Suffix ".a")
+    list(GET CMAKE_FIND_LIBRARY_PREFIXES 0 Prefix)
+  endif()
+
+  set(GDCM_LIBRARIES
+    # WARNING: The order of the libraries below *is* important!
+    ${Prefix}gdcmMSFF${Suffix}
+    ${Prefix}gdcmcharls${Suffix}
+    ${Prefix}gdcmDICT${Suffix}
+    ${Prefix}gdcmDSED${Suffix}
+    ${Prefix}gdcmIOD${Suffix}
+    ${Prefix}gdcmjpeg8${Suffix}
+    ${Prefix}gdcmjpeg12${Suffix}
+    ${Prefix}gdcmjpeg16${Suffix}
+    ${Prefix}gdcmopenjp2${Suffix}
+    ${Prefix}gdcmzlib${Suffix}
+    ${Prefix}gdcmCommon${Suffix}
+    ${Prefix}gdcmexpat${Suffix}
+
+    #${Prefix}socketxx${Suffix}
+    #${Prefix}gdcmMEXD${Suffix}  # DICOM Networking, unneeded by Orthanc plugins
+    #${Prefix}gdcmgetopt${Suffix}
+    #${Prefix}gdcmuuid${Suffix}
+    )
+
+  ExternalProject_Get_Property(GDCM binary_dir)
+  include_directories(${binary_dir}/Source/Common)
+  link_directories(${binary_dir}/bin)
+
+  ExternalProject_Get_Property(GDCM source_dir)
+  include_directories(
+    ${source_dir}/Source/Common
+    ${source_dir}/Source/MediaStorageAndFileFormat
+    ${source_dir}/Source/DataStructureAndEncodingDefinition
+    )
+
+else()
+  find_package(GDCM REQUIRED)
+  if (GDCM_FOUND)
+    include(${GDCM_USE_FILE})
+    set(GDCM_LIBRARIES gdcmCommon gdcmMSFF)
+  else(GDCM_FOUND)
+    message(FATAL_ERROR "Cannot find GDCM, did you set GDCM_DIR?")
+  endif(GDCM_FOUND)
+endif()
--- a/Plugins/Samples/GdcmDecoder/GdcmDecoderCache.cpp	Mon May 11 12:12:23 2020 +0200
+++ b/Plugins/Samples/GdcmDecoder/GdcmDecoderCache.cpp	Mon May 11 15:13:16 2020 +0200
@@ -22,17 +22,15 @@
 #include "GdcmDecoderCache.h"
 
 #include "../../../Core/Compatibility.h"
-#include "OrthancImageWrapper.h"
 
 namespace OrthancPlugins
 {
-  std::string GdcmDecoderCache::ComputeMd5(OrthancPluginContext* context,
-                                           const void* dicom,
+  std::string GdcmDecoderCache::ComputeMd5(const void* dicom,
                                            size_t size)
   {
     std::string result;
 
-    char* md5 = OrthancPluginComputeMd5(context, dicom, size);
+    char* md5 = OrthancPluginComputeMd5(OrthancPlugins::GetGlobalContext(), dicom, size);
 
     if (md5 == NULL)
     {
@@ -49,7 +47,7 @@
     {
     }
 
-    OrthancPluginFreeString(context, md5);
+    OrthancPluginFreeString(OrthancPlugins::GetGlobalContext(), md5);
 
     if (!ok)
     {
@@ -62,12 +60,11 @@
   }
 
 
-  OrthancImageWrapper* GdcmDecoderCache::Decode(OrthancPluginContext* context,
-                                                const void* dicom,
-                                                const uint32_t size,
-                                                uint32_t frameIndex)
+  OrthancImage* GdcmDecoderCache::Decode(const void* dicom,
+                                         const uint32_t size,
+                                         uint32_t frameIndex)
   {
-    std::string md5 = ComputeMd5(context, dicom, size);
+    std::string md5 = ComputeMd5(dicom, size);
 
     // First check whether the previously decoded image is the same
     // as this one
@@ -79,13 +76,13 @@
           md5_ == md5)
       {
         // This is the same image: Reuse the previous decoding
-        return new OrthancImageWrapper(context, decoder_->Decode(context, frameIndex));
+        return new OrthancImage(decoder_->Decode(frameIndex));
       }
     }
 
     // This is not the same image
     std::unique_ptr<GdcmImageDecoder> decoder(new GdcmImageDecoder(dicom, size));
-    std::unique_ptr<OrthancImageWrapper> image(new OrthancImageWrapper(context, decoder->Decode(context, frameIndex)));
+    std::unique_ptr<OrthancImage> image(new OrthancImage(decoder->Decode(frameIndex)));
 
     {
       // Cache the newly created decoder for further use
--- a/Plugins/Samples/GdcmDecoder/GdcmDecoderCache.h	Mon May 11 12:12:23 2020 +0200
+++ b/Plugins/Samples/GdcmDecoder/GdcmDecoderCache.h	Mon May 11 15:13:16 2020 +0200
@@ -23,7 +23,7 @@
 
 #include "../../../Core/Compatibility.h"
 #include "GdcmImageDecoder.h"
-#include "OrthancImageWrapper.h"
+#include "../Common/OrthancPluginCppWrapper.h"
 
 #include <boost/thread.hpp>
 
@@ -38,8 +38,7 @@
     size_t       size_;
     std::string  md5_;
 
-    static std::string ComputeMd5(OrthancPluginContext* context,
-                                  const void* dicom,
+    static std::string ComputeMd5(const void* dicom,
                                   size_t size);
 
   public:
@@ -47,9 +46,8 @@
     {
     }
 
-    OrthancImageWrapper* Decode(OrthancPluginContext* context,
-                                const void* dicom,
-                                const uint32_t size,
-                                uint32_t frameIndex);
+    OrthancImage* Decode(const void* dicom,
+                         const uint32_t size,
+                         uint32_t frameIndex);
   };
 }
--- a/Plugins/Samples/GdcmDecoder/GdcmImageDecoder.cpp	Mon May 11 12:12:23 2020 +0200
+++ b/Plugins/Samples/GdcmDecoder/GdcmImageDecoder.cpp	Mon May 11 15:13:16 2020 +0200
@@ -22,7 +22,6 @@
 #include "GdcmImageDecoder.h"
 
 #include "../../../Core/Compatibility.h"
-#include "OrthancImageWrapper.h"
 
 #include <gdcmImageReader.h>
 #include <gdcmImageApplyLookupTable.h>
@@ -298,7 +297,7 @@
   }
 
   
-  static void FixPhotometricInterpretation(OrthancImageWrapper& image,
+  static void FixPhotometricInterpretation(OrthancImage& image,
                                            gdcm::PhotometricInterpretation interpretation)
   {
     switch (interpretation)
@@ -317,7 +316,7 @@
         uint32_t pitch = image.GetPitch();
         uint8_t* buffer = reinterpret_cast<uint8_t*>(image.GetBuffer());
         
-        if (image.GetFormat() != OrthancPluginPixelFormat_RGB24 ||
+        if (image.GetPixelFormat() != OrthancPluginPixelFormat_RGB24 ||
             pitch < 3 * width)
         {
           throw std::runtime_error("Internal error");
@@ -346,8 +345,7 @@
   }
 
 
-  OrthancPluginImage* GdcmImageDecoder::Decode(OrthancPluginContext* context,
-                                               unsigned int frameIndex) const
+  OrthancPluginImage* GdcmImageDecoder::Decode(unsigned int frameIndex) const
   {
     unsigned int frames = GetFramesCount();
     unsigned int width = GetWidth();
@@ -361,7 +359,7 @@
     }
 
     std::string& decoded = pimpl_->decoded_;
-    OrthancImageWrapper target(context, format, width, height);
+    OrthancImage target(format, width, height);
 
     if (width == 0 ||
         height == 0)
@@ -391,8 +389,9 @@
       size_t targetPitch = target.GetPitch();
       size_t sourcePitch = width * bpp;
 
-      const char* a = &decoded[sourcePitch * height * frameIndex];
-      char* b = target.GetBuffer();
+      const uint8_t* a = (reinterpret_cast<const uint8_t*>(decoded.c_str()) +
+                          sourcePitch * height * frameIndex);
+      uint8_t* b = reinterpret_cast<uint8_t*>(target.GetBuffer());
 
       for (uint32_t y = 0; y < height; y++)
       {
--- a/Plugins/Samples/GdcmDecoder/GdcmImageDecoder.h	Mon May 11 12:12:23 2020 +0200
+++ b/Plugins/Samples/GdcmDecoder/GdcmImageDecoder.h	Mon May 11 15:13:16 2020 +0200
@@ -21,23 +21,13 @@
 
 #pragma once
 
-#include <orthanc/OrthancCPlugin.h>
+#include "../Common/OrthancPluginCppWrapper.h"
+
 #include <stdint.h>
 #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
@@ -60,7 +50,6 @@
 
     static size_t GetBytesPerPixel(OrthancPluginPixelFormat format);
 
-    OrthancPluginImage* Decode(OrthancPluginContext* context,
-                               unsigned int frameIndex) const;
+    OrthancPluginImage* Decode(unsigned int frameIndex) const;
   };
 }
--- a/Plugins/Samples/GdcmDecoder/OrthancImageWrapper.cpp	Mon May 11 12:12:23 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,100 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- * 
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * 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 "OrthancImageWrapper.h"
-
-#include <stdexcept>
-
-namespace OrthancPlugins
-{
-  OrthancImageWrapper::OrthancImageWrapper(OrthancPluginContext* context,
-                                           OrthancPluginPixelFormat format,
-                                           uint32_t width,
-                                           uint32_t height) :
-    context_(context)
-  {
-    image_ = OrthancPluginCreateImage(context_, format, width, height);
-    if (image_ == NULL)
-    {
-      throw std::runtime_error("Cannot create an image");
-    }
-  }
-
-
-  OrthancImageWrapper::OrthancImageWrapper(OrthancPluginContext* context,
-                                           OrthancPluginImage* image) :
-    context_(context),
-    image_(image)
-  {
-    if (image_ == NULL)
-    {
-      throw std::runtime_error("Invalid image returned by the core of Orthanc");
-    }
-  }
-
-
-
-  OrthancImageWrapper::~OrthancImageWrapper()
-  {
-    if (image_ != NULL)
-    {
-      OrthancPluginFreeImage(context_, image_);
-    }
-  }
-
-
-  OrthancPluginImage* OrthancImageWrapper::Release()
-  {
-    OrthancPluginImage* tmp = image_;
-    image_ = NULL;
-    return tmp;
-  }
-
-
-  uint32_t OrthancImageWrapper::GetWidth()
-  {
-    return OrthancPluginGetImageWidth(context_, image_);
-  }
-
-
-  uint32_t OrthancImageWrapper::GetHeight()
-  {
-    return OrthancPluginGetImageHeight(context_, image_);
-  }
-
-
-  uint32_t OrthancImageWrapper::GetPitch()
-  {
-    return OrthancPluginGetImagePitch(context_, image_);
-  }
-
-
-  OrthancPluginPixelFormat OrthancImageWrapper::GetFormat()
-  {
-    return OrthancPluginGetImagePixelFormat(context_, image_);
-  }
-
-
-  char* OrthancImageWrapper::GetBuffer()
-  {
-    return reinterpret_cast<char*>(OrthancPluginGetImageBuffer(context_, image_));
-  }
-}
--- a/Plugins/Samples/GdcmDecoder/OrthancImageWrapper.h	Mon May 11 12:12:23 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/**
- * Orthanc - A Lightweight, RESTful DICOM Store
- * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
- * Copyright (C) 2017-2020 Osimis S.A., Belgium
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- * 
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * 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 <orthanc/OrthancCPlugin.h>
-
-#include "GdcmImageDecoder.h"
-
-namespace OrthancPlugins
-{
-  class OrthancImageWrapper
-  {
-  private:
-    OrthancPluginContext*  context_;
-    OrthancPluginImage*    image_;
-
-  public:
-    OrthancImageWrapper(OrthancPluginContext* context,
-                        OrthancPluginPixelFormat format,
-                        uint32_t width,
-                        uint32_t height);
-
-    OrthancImageWrapper(OrthancPluginContext* context,
-                        OrthancPluginImage* image);  // Takes ownership
-
-    ~OrthancImageWrapper();
-
-    OrthancPluginContext* GetContext()
-    {
-      return context_;
-    }
-
-    OrthancPluginImage* Release();
-
-    uint32_t GetWidth();
-
-    uint32_t GetHeight();
-
-    uint32_t GetPitch();
-
-    OrthancPluginPixelFormat GetFormat();
-
-    char* GetBuffer();
-  };
-}
--- a/Plugins/Samples/GdcmDecoder/Plugin.cpp	Mon May 11 12:12:23 2020 +0200
+++ b/Plugins/Samples/GdcmDecoder/Plugin.cpp	Mon May 11 15:13:16 2020 +0200
@@ -20,13 +20,81 @@
 
 
 #include "../../../Core/Compatibility.h"
+#include "../../../Core/DicomFormat/DicomMap.h"
+#include "../../../Core/Toolbox.h"
 #include "GdcmDecoderCache.h"
-#include "OrthancImageWrapper.h"
+
+static OrthancPlugins::GdcmDecoderCache  cache_;
+static bool restrictTransferSyntaxes_ = false;
+static std::set<std::string> enabledTransferSyntaxes_;
+
+
+static bool ExtractTransferSyntax(std::string& transferSyntax,
+                                  const void* dicom,
+                                  const uint32_t size)
+{
+  Orthanc::DicomMap header;
+  if (!Orthanc::DicomMap::ParseDicomMetaInformation(header, reinterpret_cast<const char*>(dicom), size))
+  {
+    return false;
+  }
+
+  const Orthanc::DicomValue* tag = header.TestAndGetValue(0x0002, 0x0010);
+  if (tag == NULL ||
+      tag->IsNull() ||
+      tag->IsBinary())
+  {
+    return false;
+  }
+  else
+  {
+    // Stripping spaces should not be required, as this is a UI value
+    // representation whose stripping is supported by the Orthanc
+    // core, but let's be careful...
+    transferSyntax = Orthanc::Toolbox::StripSpaces(tag->GetContent());
+    return true;
+  }
+}
+
 
-#include <orthanc/OrthancCPlugin.h>
+static bool IsTransferSyntaxEnabled(const void* dicom,
+                                    const uint32_t size)
+{
+  std::string formattedSize;
+
+  {
+    char tmp[16];
+    sprintf(tmp, "%0.1fMB", static_cast<float>(size) / (1024.0f * 1024.0f));
+    formattedSize.assign(tmp);
+  }
+
+  if (!restrictTransferSyntaxes_)
+  {
+    LOG(INFO) << "Decoding one DICOM instance of " << formattedSize << " using GDCM";
+    return true;
+  }
 
-static OrthancPluginContext* context_ = NULL;
-static OrthancPlugins::GdcmDecoderCache  cache_;
+  std::string transferSyntax;
+  if (!ExtractTransferSyntax(transferSyntax, dicom, size))
+  {
+    LOG(INFO) << "Cannot extract the transfer syntax of this instance of "
+              << formattedSize << ", will use GDCM to decode it";
+    return true;
+  }
+  else if (enabledTransferSyntaxes_.find(transferSyntax) != enabledTransferSyntaxes_.end())
+  {
+    // Decoding for this transfer syntax is enabled
+    LOG(INFO) << "Using GDCM to decode this instance of " << formattedSize
+              << " with transfer syntax " << transferSyntax;
+    return true;
+  }
+  else
+  {
+    LOG(INFO) << "Won't use GDCM to decode this instance of " << formattedSize
+              << ", as its transfer syntax " << transferSyntax << " is disabled";
+    return false;
+  }
+}
 
 
 static OrthancPluginErrorCode DecodeImageCallback(OrthancPluginImage** target,
@@ -36,62 +104,135 @@
 {
   try
   {
-    std::unique_ptr<OrthancPlugins::OrthancImageWrapper> image;
+    if (!IsTransferSyntaxEnabled(dicom, size))
+    {
+      *target = NULL;
+      return OrthancPluginErrorCode_Success;
+    }
+
+    std::unique_ptr<OrthancPlugins::OrthancImage> image;
 
 #if 0
     // Do not use the cache
     OrthancPlugins::GdcmImageDecoder decoder(dicom, size);
-    image.reset(new OrthancPlugins::OrthancImageWrapper(context_, decoder.Decode(context_, frameIndex)));
+    image.reset(new OrthancPlugins::OrthancImage(decoder.Decode(frameIndex)));
 #else
-    image.reset(cache_.Decode(context_, dicom, size, frameIndex));
+    image.reset(cache_.Decode(dicom, size, frameIndex));
 #endif
 
     *target = image->Release();
 
     return OrthancPluginErrorCode_Success;
   }
+  catch (Orthanc::OrthancException& e)
+  {
+    *target = NULL;
+
+    LOG(WARNING) << "Cannot decode image using GDCM: " << e.What();
+    return OrthancPluginErrorCode_Plugin;
+  }
   catch (std::runtime_error& e)
   {
     *target = NULL;
 
-    std::string s = "Cannot decode image using GDCM: " + std::string(e.what());
-    OrthancPluginLogInfo(context_, s.c_str());
+    LOG(WARNING) << "Cannot decode image using GDCM: " << e.what();
+    return OrthancPluginErrorCode_Plugin;
+  }
+  catch (...)
+  {
+    *target = NULL;
+
+    LOG(WARNING) << "Native exception while decoding image using GDCM";
     return OrthancPluginErrorCode_Plugin;
   }
 }
 
 
 
+/**
+ * We force the redefinition of the "ORTHANC_PLUGINS_API" macro, that
+ * was left empty with gcc until Orthanc SDK 1.5.7 (no "default"
+ * visibility). This causes the version script, if run from "Holy
+ * Build Box", to make private the 4 global functions of the plugin.
+ **/
+
+#undef ORTHANC_PLUGINS_API
+
+#ifdef WIN32
+#  define ORTHANC_PLUGINS_API __declspec(dllexport)
+#elif __GNUC__ >= 4
+#  define ORTHANC_PLUGINS_API __attribute__ ((visibility ("default")))
+#else
+#  define ORTHANC_PLUGINS_API
+#endif
+
+
 extern "C"
 {
   ORTHANC_PLUGINS_API int32_t OrthancPluginInitialize(OrthancPluginContext* context)
   {
-    context_ = context;
-    OrthancPluginLogWarning(context_, "Initializing the advanced decoder of medical images using GDCM");
+    static const char* const KEY_GDCM = "Gdcm";
+    static const char* const KEY_ENABLE_GDCM = "EnableGdcm";
+    static const char* const KEY_RESTRICT_TRANSFER_SYNTAXES = "RestrictTransferSyntaxes";
+
+    OrthancPlugins::SetGlobalContext(context);
+    LOG(INFO) << "Initializing the advanced decoder of medical images using GDCM";
 
 
     /* Check the version of the Orthanc core */
-    if (OrthancPluginCheckVersion(context_) == 0)
+    if (OrthancPluginCheckVersion(context) == 0)
     {
       char info[1024];
       sprintf(info, "Your version of Orthanc (%s) must be above %d.%d.%d to run this plugin",
-              context_->orthancVersion,
+              context->orthancVersion,
               ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER,
               ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER,
               ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER);
-      OrthancPluginLogError(context_, info);
+      OrthancPluginLogError(context, info);
       return -1;
     }
 
-    OrthancPluginSetDescription(context_, "Advanced decoder of medical images using GDCM.");
-    OrthancPluginRegisterDecodeImageCallback(context_, DecodeImageCallback);
+    OrthancPluginSetDescription(context, "Advanced decoder of medical images using GDCM.");
+
+    OrthancPlugins::OrthancConfiguration global;
+
+    bool enabled = true;
+    
+    if (global.IsSection(KEY_GDCM))
+    {
+      OrthancPlugins::OrthancConfiguration config;
+      global.GetSection(config, KEY_GDCM);
+
+      enabled = config.GetBooleanValue(KEY_ENABLE_GDCM, true);
 
+      if (config.LookupSetOfStrings(enabledTransferSyntaxes_, KEY_RESTRICT_TRANSFER_SYNTAXES, false))
+      {
+        restrictTransferSyntaxes_ = true;
+        
+        for (std::set<std::string>::const_iterator it = enabledTransferSyntaxes_.begin();
+             it != enabledTransferSyntaxes_.end(); ++it)
+        {
+          LOG(WARNING) << "Orthanc will use GDCM to decode transfer syntax: " << *it;
+        }
+      }
+    }
+
+    if (enabled)
+    {
+      OrthancPluginRegisterDecodeImageCallback(context, DecodeImageCallback);
+    }
+    else
+    {
+      LOG(WARNING) << "The advanced decoder of medical images using GDCM is disabled";
+    }
+    
     return 0;
   }
 
 
   ORTHANC_PLUGINS_API void OrthancPluginFinalize()
   {
+    LOG(INFO) << "Finalizing the advanced decoder of medical images using GDCM";
   }
 
 
@@ -103,6 +244,6 @@
 
   ORTHANC_PLUGINS_API const char* OrthancPluginGetVersion()
   {
-    return GDCM_DECODER_VERSION;
+    return PLUGIN_VERSION;
   }
 }