changeset 49:b8b1ea028da9

support of WADO
author Sebastien Jodogne <s.jodogne@gmail.com>
date Sun, 02 Aug 2015 11:14:19 +0200
parents 42ddc9bbc168
children 69b004011c4b
files CMakeLists.txt NEWS Orthanc/Core/ImageFormats/ImageAccessor.cpp Orthanc/Core/ImageFormats/ImageAccessor.h Orthanc/Core/ImageFormats/ImageBuffer.cpp Orthanc/Core/ImageFormats/ImageBuffer.h Orthanc/Core/ImageFormats/PngReader.cpp Orthanc/Core/ImageFormats/PngReader.h Orthanc/Resources/CMake/LibPngConfiguration.cmake Plugin/Configuration.cpp Plugin/Configuration.h Plugin/JpegWriter.cpp Plugin/JpegWriter.h Plugin/Plugin.cpp Plugin/Wado.cpp Plugin/Wado.h Resources/CMake/LibJpegConfiguration.cmake Resources/SyncOrthancFolder.py
diffstat 18 files changed, 1952 insertions(+), 44 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Sun Aug 02 09:34:56 2015 +0200
+++ b/CMakeLists.txt	Sun Aug 02 11:14:19 2015 +0200
@@ -32,7 +32,9 @@
 set(USE_SYSTEM_GDCM ON CACHE BOOL "Use the system version of Grassroot DICOM (GDCM)")
 set(USE_SYSTEM_GOOGLE_TEST ON CACHE BOOL "Use the system version of Google Test")
 set(USE_SYSTEM_JSONCPP ON CACHE BOOL "Use the system version of JsonCpp")
-SET(USE_SYSTEM_PUGIXML ON CACHE BOOL "Use the system version of Pugixml)")
+set(USE_SYSTEM_LIBJPEG ON CACHE BOOL "Use the system version of libjpeg")
+set(USE_SYSTEM_LIBPNG ON CACHE BOOL "Use the system version of libpng")
+SET(USE_SYSTEM_PUGIXML ON CACHE BOOL "Use the system version of Pugixml")
 
 # Distribution-specific settings
 set(USE_GTEST_DEBIAN_SOURCE_PACKAGE OFF CACHE BOOL "Use the sources of Google Test shipped with libgtest-dev (Debian only)")
@@ -50,9 +52,11 @@
 include(${ORTHANC_ROOT}/Resources/CMake/BoostConfiguration.cmake)
 include(${ORTHANC_ROOT}/Resources/CMake/GoogleTestConfiguration.cmake)
 include(${ORTHANC_ROOT}/Resources/CMake/JsonCppConfiguration.cmake)
+include(${ORTHANC_ROOT}/Resources/CMake/LibPngConfiguration.cmake)
 include(${ORTHANC_ROOT}/Resources/CMake/PugixmlConfiguration.cmake)
 
 include(${CMAKE_SOURCE_DIR}/Resources/CMake/GdcmConfiguration.cmake)
+include(${CMAKE_SOURCE_DIR}/Resources/CMake/LibJpegConfiguration.cmake)
 
 
 if (STATIC_BUILD)
@@ -101,10 +105,15 @@
 set(CORE_SOURCES
   ${BOOST_SOURCES}
   ${JSONCPP_SOURCES}
+  ${LIBJPEG_SOURCES}
+  ${LIBPNG_SOURCES}
   ${PUGIXML_SOURCES}
 
   ${ORTHANC_ROOT}/Core/ChunkedBuffer.cpp
   ${ORTHANC_ROOT}/Core/Enumerations.cpp
+  ${ORTHANC_ROOT}/Core/ImageFormats/ImageAccessor.cpp
+  ${ORTHANC_ROOT}/Core/ImageFormats/ImageBuffer.cpp
+  ${ORTHANC_ROOT}/Core/ImageFormats/PngReader.cpp
   ${ORTHANC_ROOT}/Core/OrthancException.cpp
   ${ORTHANC_ROOT}/Core/Toolbox.cpp
   ${ORTHANC_ROOT}/Resources/ThirdParty/base64/base64.cpp
@@ -114,12 +123,14 @@
   Plugin/Configuration.cpp
   Plugin/Dicom.cpp
   Plugin/DicomResults.cpp
+  Plugin/JpegWriter.cpp
   )
 
 add_library(OrthancDicomWeb SHARED ${CORE_SOURCES}
   ${CMAKE_SOURCE_DIR}/Plugin/Plugin.cpp
   ${CMAKE_SOURCE_DIR}/Plugin/QidoRs.cpp
   ${CMAKE_SOURCE_DIR}/Plugin/StowRs.cpp
+  ${CMAKE_SOURCE_DIR}/Plugin/Wado.cpp
   ${CMAKE_SOURCE_DIR}/Plugin/WadoRs.cpp
   ${AUTOGENERATED_SOURCES}
   )
--- a/NEWS	Sun Aug 02 09:34:56 2015 +0200
+++ b/NEWS	Sun Aug 02 11:14:19 2015 +0200
@@ -3,6 +3,7 @@
 
 No official release yet. Still work in progress.
 
+* Support of WADO, in addition to DICOMweb
 * All the APIs are now under the same root
 * Inject version information into Windows binaries
 * Use of Orthanc built-in API for multipart answers (requires Orthanc >= 0.9.1)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Orthanc/Core/ImageFormats/ImageAccessor.cpp	Sun Aug 02 11:14:19 2015 +0200
@@ -0,0 +1,231 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 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 "../PrecompiledHeaders.h"
+#include "ImageAccessor.h"
+
+#include "../OrthancException.h"
+#include "../ChunkedBuffer.h"
+
+#include <stdint.h>
+#include <cassert>
+#include <boost/lexical_cast.hpp>
+
+#if HAVE_GOOGLE_LOG == 1
+#include <glog/logging.h>
+#endif
+
+
+namespace Orthanc
+{
+  template <typename PixelType>
+  static void ToMatlabStringInternal(ChunkedBuffer& target,
+                                     const ImageAccessor& source)
+  {
+    target.AddChunk("double([ ");
+
+    for (unsigned int y = 0; y < source.GetHeight(); y++)
+    {
+      const PixelType* p = reinterpret_cast<const PixelType*>(source.GetConstRow(y));
+
+      std::string s;
+      if (y > 0)
+      {
+        s = "; ";
+      }
+
+      s.reserve(source.GetWidth() * 8);
+
+      for (unsigned int x = 0; x < source.GetWidth(); x++, p++)
+      {
+        s += boost::lexical_cast<std::string>(static_cast<int>(*p)) + " ";
+      }
+
+      target.AddChunk(s);
+    }
+
+    target.AddChunk("])");
+  }
+
+
+  static void RGB24ToMatlabString(ChunkedBuffer& target,
+                                  const ImageAccessor& source)
+  {
+    assert(source.GetFormat() == PixelFormat_RGB24);
+
+    target.AddChunk("double(permute(reshape([ ");
+
+    for (unsigned int y = 0; y < source.GetHeight(); y++)
+    {
+      const uint8_t* p = reinterpret_cast<const uint8_t*>(source.GetConstRow(y));
+      
+      std::string s;
+      s.reserve(source.GetWidth() * 3 * 8);
+      
+      for (unsigned int x = 0; x < 3 * source.GetWidth(); x++, p++)
+      {
+        s += boost::lexical_cast<std::string>(static_cast<int>(*p)) + " ";
+      }
+      
+      target.AddChunk(s);
+    }
+
+    target.AddChunk("], [ 3 " + boost::lexical_cast<std::string>(source.GetHeight()) +
+                    " " + boost::lexical_cast<std::string>(source.GetWidth()) + " ]), [ 3 2 1 ]))");
+  }
+
+
+  void* ImageAccessor::GetBuffer() const
+  {
+    if (readOnly_)
+    {
+#if HAVE_GOOGLE_LOG == 1
+      LOG(ERROR) << "Trying to write on a read-only image";
+#endif
+
+      throw OrthancException(ErrorCode_ReadOnly);
+    }
+
+    return buffer_;
+  }
+
+
+  const void* ImageAccessor::GetConstRow(unsigned int y) const
+  {
+    if (buffer_ != NULL)
+    {
+      return reinterpret_cast<const uint8_t*>(buffer_) + y * pitch_;
+    }
+    else
+    {
+      return NULL;
+    }
+  }
+
+
+  void* ImageAccessor::GetRow(unsigned int y) const
+  {
+    if (readOnly_)
+    {
+#if HAVE_GOOGLE_LOG == 1
+      LOG(ERROR) << "Trying to write on a read-only image";
+#endif
+
+      throw OrthancException(ErrorCode_ReadOnly);
+    }
+
+    if (buffer_ != NULL)
+    {
+      return reinterpret_cast<uint8_t*>(buffer_) + y * pitch_;
+    }
+    else
+    {
+      return NULL;
+    }
+  }
+
+
+  void ImageAccessor::AssignEmpty(PixelFormat format)
+  {
+    readOnly_ = false;
+    format_ = format;
+    width_ = 0;
+    height_ = 0;
+    pitch_ = 0;
+    buffer_ = NULL;
+  }
+
+
+  void ImageAccessor::AssignReadOnly(PixelFormat format,
+                                     unsigned int width,
+                                     unsigned int height,
+                                     unsigned int pitch,
+                                     const void *buffer)
+  {
+    readOnly_ = true;
+    format_ = format;
+    width_ = width;
+    height_ = height;
+    pitch_ = pitch;
+    buffer_ = const_cast<void*>(buffer);
+
+    assert(GetBytesPerPixel() * width_ <= pitch_);
+  }
+
+
+  void ImageAccessor::AssignWritable(PixelFormat format,
+                                     unsigned int width,
+                                     unsigned int height,
+                                     unsigned int pitch,
+                                     void *buffer)
+  {
+    readOnly_ = false;
+    format_ = format;
+    width_ = width;
+    height_ = height;
+    pitch_ = pitch;
+    buffer_ = buffer;
+
+    assert(GetBytesPerPixel() * width_ <= pitch_);
+  }
+
+
+  void ImageAccessor::ToMatlabString(std::string& target) const
+  {
+    ChunkedBuffer buffer;
+
+    switch (GetFormat())
+    {
+      case PixelFormat_Grayscale8:
+        ToMatlabStringInternal<uint8_t>(buffer, *this);
+        break;
+
+      case PixelFormat_Grayscale16:
+        ToMatlabStringInternal<uint16_t>(buffer, *this);
+        break;
+
+      case PixelFormat_SignedGrayscale16:
+        ToMatlabStringInternal<int16_t>(buffer, *this);
+        break;
+
+      case PixelFormat_RGB24:
+        RGB24ToMatlabString(buffer, *this);
+        break;
+
+      default:
+        throw OrthancException(ErrorCode_NotImplemented);
+    }   
+
+    buffer.Flatten(target);
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Orthanc/Core/ImageFormats/ImageAccessor.h	Sun Aug 02 11:14:19 2015 +0200
@@ -0,0 +1,119 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 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 "../Enumerations.h"
+
+#include <string>
+
+namespace Orthanc
+{
+  class ImageAccessor
+  {
+  private:
+    bool readOnly_;
+    PixelFormat format_;
+    unsigned int width_;
+    unsigned int height_;
+    unsigned int pitch_;
+    void *buffer_;
+
+  public:
+    ImageAccessor()
+    {
+      AssignEmpty(PixelFormat_Grayscale8);
+    }
+
+    bool IsReadOnly() const
+    {
+      return readOnly_;
+    }
+
+    PixelFormat GetFormat() const
+    {
+      return format_;
+    }
+
+    unsigned int GetBytesPerPixel() const
+    {
+      return ::Orthanc::GetBytesPerPixel(format_);
+    }
+
+    unsigned int GetWidth() const
+    {
+      return width_;
+    }
+
+    unsigned int GetHeight() const
+    {
+      return height_;
+    }
+
+    unsigned int GetPitch() const
+    {
+      return pitch_;
+    }
+
+    unsigned int GetSize() const
+    {
+      return GetHeight() * GetPitch();
+    }
+
+    const void* GetConstBuffer() const
+    {
+      return buffer_;
+    }
+
+    void* GetBuffer() const;
+
+    const void* GetConstRow(unsigned int y) const;
+
+    void* GetRow(unsigned int y) const;
+
+    void AssignEmpty(PixelFormat format);
+
+    void AssignReadOnly(PixelFormat format,
+                        unsigned int width,
+                        unsigned int height,
+                        unsigned int pitch,
+                        const void *buffer);
+
+    void AssignWritable(PixelFormat format,
+                        unsigned int width,
+                        unsigned int height,
+                        unsigned int pitch,
+                        void *buffer);
+
+    void ToMatlabString(std::string& target) const; 
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Orthanc/Core/ImageFormats/ImageBuffer.cpp	Sun Aug 02 11:14:19 2015 +0200
@@ -0,0 +1,192 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 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 "../PrecompiledHeaders.h"
+#include "ImageBuffer.h"
+
+#include "../OrthancException.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+namespace Orthanc
+{
+  void ImageBuffer::Allocate()
+  {
+    if (changed_)
+    {
+      Deallocate();
+
+      /*
+        if (forceMinimalPitch_)
+        {
+        TODO: Align pitch and memory buffer to optimal size for SIMD.
+        }
+      */
+
+      pitch_ = GetBytesPerPixel() * width_;
+      size_t size = pitch_ * height_;
+
+      if (size == 0)
+      {
+        buffer_ = NULL;
+      }
+      else
+      {
+        buffer_ = malloc(size);
+        if (buffer_ == NULL)
+        {
+          throw OrthancException(ErrorCode_NotEnoughMemory);
+        }
+      }
+
+      changed_ = false;
+    }
+  }
+
+
+  void ImageBuffer::Deallocate()
+  {
+    if (buffer_ != NULL)
+    {
+      free(buffer_);
+      buffer_ = NULL;
+      changed_ = true;
+    }
+  }
+
+
+  ImageBuffer::ImageBuffer(unsigned int width,
+                           unsigned int height,
+                           PixelFormat format)
+  {
+    Initialize();
+    SetWidth(width);
+    SetHeight(height);
+    SetFormat(format);
+  }
+
+
+  void ImageBuffer::Initialize()
+  {
+    changed_ = false;
+    forceMinimalPitch_ = true;
+    format_ = PixelFormat_Grayscale8;
+    width_ = 0;
+    height_ = 0;
+    pitch_ = 0;
+    buffer_ = NULL;
+  }
+
+
+  void ImageBuffer::SetFormat(PixelFormat format)
+  {
+    if (format != format_)
+    {
+      changed_ = true;
+      format_ = format;
+    }
+  }
+
+
+  void ImageBuffer::SetWidth(unsigned int width)
+  {
+    if (width != width_)
+    {
+      changed_ = true;
+      width_ = width;     
+    }
+  }
+
+
+  void ImageBuffer::SetHeight(unsigned int height)
+  {
+    if (height != height_)
+    {
+      changed_ = true;
+      height_ = height;     
+    }
+  }
+
+
+  ImageAccessor ImageBuffer::GetAccessor()
+  {
+    Allocate();
+
+    ImageAccessor accessor;
+    accessor.AssignWritable(format_, width_, height_, pitch_, buffer_);
+    return accessor;
+  }
+
+
+  ImageAccessor ImageBuffer::GetConstAccessor()
+  {
+    Allocate();
+
+    ImageAccessor accessor;
+    accessor.AssignReadOnly(format_, width_, height_, pitch_, buffer_);
+    return accessor;
+  }
+
+
+  void ImageBuffer::SetMinimalPitchForced(bool force)
+  {
+    if (force != forceMinimalPitch_)
+    {
+      changed_ = true;
+      forceMinimalPitch_ = force;
+    }
+  }
+
+
+  void ImageBuffer::AcquireOwnership(ImageBuffer& other)
+  {
+    // Remove the content of the current image
+    Deallocate();
+
+    // Force the allocation of the other image (if not already
+    // allocated)
+    other.Allocate();
+
+    // Transfer the content of the other image
+    changed_ = false;
+    forceMinimalPitch_ = other.forceMinimalPitch_;
+    format_ = other.format_;
+    width_ = other.width_;
+    height_ = other.height_;
+    pitch_ = other.pitch_;
+    buffer_ = other.buffer_;
+
+    // Force the reinitialization of the other image
+    other.Initialize();
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Orthanc/Core/ImageFormats/ImageBuffer.h	Sun Aug 02 11:14:19 2015 +0200
@@ -0,0 +1,115 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 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 "ImageAccessor.h"
+
+#include <vector>
+#include <stdint.h>
+#include <boost/noncopyable.hpp>
+
+namespace Orthanc
+{
+  class ImageBuffer : public boost::noncopyable
+  {
+  private:
+    bool changed_;
+
+    bool forceMinimalPitch_;  // Currently unused
+    PixelFormat format_;
+    unsigned int width_;
+    unsigned int height_;
+    unsigned int pitch_;
+    void *buffer_;
+
+    void Initialize();
+    
+    void Allocate();
+
+    void Deallocate();
+
+  public:
+    ImageBuffer(unsigned int width,
+                unsigned int height,
+                PixelFormat format);
+
+    ImageBuffer()
+    {
+      Initialize();
+    }
+
+    ~ImageBuffer()
+    {
+      Deallocate();
+    }
+
+    PixelFormat GetFormat() const
+    {
+      return format_;
+    }
+
+    void SetFormat(PixelFormat format);
+
+    unsigned int GetWidth() const
+    {
+      return width_;
+    }
+
+    void SetWidth(unsigned int width);
+
+    unsigned int GetHeight() const
+    {
+      return height_;
+    }
+
+    void SetHeight(unsigned int height);
+
+    unsigned int GetBytesPerPixel() const
+    {
+      return ::Orthanc::GetBytesPerPixel(format_);
+    }
+
+    ImageAccessor GetAccessor();
+
+    ImageAccessor GetConstAccessor();
+
+    bool IsMinimalPitchForced() const
+    {
+      return forceMinimalPitch_;
+    }
+
+    void SetMinimalPitchForced(bool force);
+
+    void AcquireOwnership(ImageBuffer& other);
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Orthanc/Core/ImageFormats/PngReader.cpp	Sun Aug 02 11:14:19 2015 +0200
@@ -0,0 +1,313 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 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 "../PrecompiledHeaders.h"
+#include "PngReader.h"
+
+#include "../OrthancException.h"
+#include "../Toolbox.h"
+
+#include <png.h>
+#include <string.h>  // For memcpy()
+
+namespace Orthanc
+{
+  namespace 
+  {
+    struct FileRabi
+    {
+      FILE* fp_;
+
+      FileRabi(const char* filename)
+      {
+        fp_ = fopen(filename, "rb");
+        if (!fp_)
+        {
+          throw OrthancException(ErrorCode_InexistentFile);
+        }
+      }
+
+      ~FileRabi()
+      {
+        if (fp_)
+          fclose(fp_);
+      }
+    };
+  }
+
+
+  struct PngReader::PngRabi
+  {
+    png_structp png_;
+    png_infop info_;
+    png_infop endInfo_;
+
+    void Destruct()
+    {
+      if (png_)
+      {
+        png_destroy_read_struct(&png_, &info_, &endInfo_);
+
+        png_ = NULL;
+        info_ = NULL;
+        endInfo_ = NULL;
+      }
+    }
+
+    PngRabi()
+    {
+      png_ = NULL;
+      info_ = NULL;
+      endInfo_ = NULL;
+
+      png_ = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+      if (!png_)
+      {
+        throw OrthancException(ErrorCode_NotEnoughMemory);
+      }
+
+      info_ = png_create_info_struct(png_);
+      if (!info_)
+      {
+        png_destroy_read_struct(&png_, NULL, NULL);
+        throw OrthancException(ErrorCode_NotEnoughMemory);
+      }
+
+      endInfo_ = png_create_info_struct(png_);
+      if (!info_)
+      {
+        png_destroy_read_struct(&png_, &info_, NULL);
+        throw OrthancException(ErrorCode_NotEnoughMemory);
+      }
+    }
+
+    ~PngRabi()
+    {
+      Destruct();
+    }
+
+    static void MemoryCallback(png_structp png_ptr, 
+                               png_bytep data, 
+                               png_size_t size);
+  };
+
+
+  void PngReader::CheckHeader(const void* header)
+  {
+    int is_png = !png_sig_cmp((png_bytep) header, 0, 8);
+    if (!is_png)
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+  }
+
+  PngReader::PngReader()
+  {
+  }
+
+  void PngReader::Read(PngRabi& rabi)
+  {
+    png_set_sig_bytes(rabi.png_, 8);
+
+    png_read_info(rabi.png_, rabi.info_);
+
+    png_uint_32 width, height;
+    int bit_depth, color_type, interlace_type;
+    int compression_type, filter_method;
+    // get size and bit-depth of the PNG-image
+    png_get_IHDR(rabi.png_, rabi.info_,
+                 &width, &height,
+                 &bit_depth, &color_type, &interlace_type,
+                 &compression_type, &filter_method);
+
+    PixelFormat format;
+    unsigned int pitch;
+
+    if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth == 8)
+    {
+      format = PixelFormat_Grayscale8;
+      pitch = width;
+    }
+    else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth == 16)
+    {
+      format = PixelFormat_Grayscale16;
+      pitch = 2 * width;
+
+      if (Toolbox::DetectEndianness() == Endianness_Little)
+      {
+        png_set_swap(rabi.png_);
+      }
+    }
+    else if (color_type == PNG_COLOR_TYPE_RGB && bit_depth == 8)
+    {
+      format = PixelFormat_RGB24;
+      pitch = 3 * width;
+    }
+    else if (color_type == PNG_COLOR_TYPE_RGBA && bit_depth == 8)
+    {
+      format = PixelFormat_RGBA32;
+      pitch = 4 * width;
+    }
+    else
+    {
+      throw OrthancException(ErrorCode_NotImplemented);
+    }
+
+    data_.resize(height * pitch);
+
+    if (height == 0 || width == 0)
+    {
+      // Empty image, we are done
+      AssignEmpty(format);
+      return;
+    }
+    
+    png_read_update_info(rabi.png_, rabi.info_);
+
+    std::vector<png_bytep> rows(height);
+    for (size_t i = 0; i < height; i++)
+    {
+      rows[i] = &data_[0] + i * pitch;
+    }
+
+    png_read_image(rabi.png_, &rows[0]);
+
+    AssignReadOnly(format, width, height, pitch, &data_[0]);
+  }
+
+  void PngReader::ReadFromFile(const char* filename)
+  {
+    FileRabi f(filename);
+
+    char header[8];
+    if (fread(header, 1, 8, f.fp_) != 8)
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+
+    CheckHeader(header);
+
+    PngRabi rabi;
+
+    if (setjmp(png_jmpbuf(rabi.png_)))
+    {
+      rabi.Destruct();
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+
+    png_init_io(rabi.png_, f.fp_);
+
+    Read(rabi);
+  }
+
+
+  namespace
+  {
+    struct MemoryBuffer
+    {
+      const uint8_t* buffer_;
+      size_t size_;
+      size_t pos_;
+      bool ok_;
+    };
+  }
+
+
+  void PngReader::PngRabi::MemoryCallback(png_structp png_ptr, 
+                                          png_bytep outBytes, 
+                                          png_size_t byteCountToRead)
+  {
+    MemoryBuffer* from = reinterpret_cast<MemoryBuffer*>(png_get_io_ptr(png_ptr));
+
+    if (!from->ok_)
+    {
+      return;
+    }
+
+    if (from->pos_ + byteCountToRead > from->size_)
+    {
+      from->ok_ = false;
+      return;
+    }
+
+    memcpy(outBytes, from->buffer_ + from->pos_, byteCountToRead);
+
+    from->pos_ += byteCountToRead;
+  }
+
+
+  void PngReader::ReadFromMemory(const void* buffer,
+                                 size_t size)
+  {
+    if (size < 8)
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+
+    CheckHeader(buffer);
+
+    PngRabi rabi;
+
+    if (setjmp(png_jmpbuf(rabi.png_)))
+    {
+      rabi.Destruct();
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+
+    MemoryBuffer tmp;
+    tmp.buffer_ = reinterpret_cast<const uint8_t*>(buffer) + 8;  // We skip the header
+    tmp.size_ = size - 8;
+    tmp.pos_ = 0;
+    tmp.ok_ = true;
+
+    png_set_read_fn(rabi.png_, &tmp, PngRabi::MemoryCallback);
+
+    Read(rabi);
+
+    if (!tmp.ok_)
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+  }
+
+  void PngReader::ReadFromMemory(const std::string& buffer)
+  {
+    if (buffer.size() != 0)
+    {
+      ReadFromMemory(&buffer[0], buffer.size());
+    }
+    else
+    {
+      ReadFromMemory(NULL, 0);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Orthanc/Core/ImageFormats/PngReader.h	Sun Aug 02 11:14:19 2015 +0200
@@ -0,0 +1,71 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 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 "ImageAccessor.h"
+
+#include "../Enumerations.h"
+
+#include <vector>
+#include <stdint.h>
+#include <boost/shared_ptr.hpp>
+
+namespace Orthanc
+{
+  class PngReader : public ImageAccessor
+  {
+  private:
+    struct PngRabi;
+
+    std::vector<uint8_t> data_;
+
+    void CheckHeader(const void* header);
+
+    void Read(PngRabi& rabi);
+
+  public:
+    PngReader();
+
+    void ReadFromFile(const char* filename);
+
+    void ReadFromFile(const std::string& filename)
+    {
+      ReadFromFile(filename.c_str());
+    }
+
+    void ReadFromMemory(const void* buffer,
+                        size_t size);
+
+    void ReadFromMemory(const std::string& buffer);
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Orthanc/Resources/CMake/LibPngConfiguration.cmake	Sun Aug 02 11:14:19 2015 +0200
@@ -0,0 +1,61 @@
+if (STATIC_BUILD OR NOT USE_SYSTEM_LIBPNG)
+  SET(LIBPNG_SOURCES_DIR ${CMAKE_BINARY_DIR}/libpng-1.5.12)
+  DownloadPackage(
+    "8ea7f60347a306c5faf70b977fa80e28"
+    "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/libpng-1.5.12.tar.gz"
+    "${LIBPNG_SOURCES_DIR}")
+
+  include_directories(
+    ${LIBPNG_SOURCES_DIR}
+    )
+
+  configure_file(
+    ${LIBPNG_SOURCES_DIR}/scripts/pnglibconf.h.prebuilt
+    ${LIBPNG_SOURCES_DIR}/pnglibconf.h
+    COPY_ONLY)
+
+  set(LIBPNG_SOURCES
+    #${LIBPNG_SOURCES_DIR}/example.c
+    ${LIBPNG_SOURCES_DIR}/png.c
+    ${LIBPNG_SOURCES_DIR}/pngerror.c
+    ${LIBPNG_SOURCES_DIR}/pngget.c
+    ${LIBPNG_SOURCES_DIR}/pngmem.c
+    ${LIBPNG_SOURCES_DIR}/pngpread.c
+    ${LIBPNG_SOURCES_DIR}/pngread.c
+    ${LIBPNG_SOURCES_DIR}/pngrio.c
+    ${LIBPNG_SOURCES_DIR}/pngrtran.c
+    ${LIBPNG_SOURCES_DIR}/pngrutil.c
+    ${LIBPNG_SOURCES_DIR}/pngset.c
+    #${LIBPNG_SOURCES_DIR}/pngtest.c
+    ${LIBPNG_SOURCES_DIR}/pngtrans.c
+    ${LIBPNG_SOURCES_DIR}/pngwio.c
+    ${LIBPNG_SOURCES_DIR}/pngwrite.c
+    ${LIBPNG_SOURCES_DIR}/pngwtran.c
+    ${LIBPNG_SOURCES_DIR}/pngwutil.c
+    )
+
+  #set_property(
+  #  SOURCE ${LIBPNG_SOURCES}
+  #  PROPERTY COMPILE_FLAGS -UHAVE_CONFIG_H)
+
+  add_definitions(
+    -DPNG_NO_CONSOLE_IO=1
+    -DPNG_NO_STDIO=1
+    # The following declaration avoids "__declspec(dllexport)" in
+    # libpng to prevent publicly exposing its symbols by the DLLs
+    -DPNG_IMPEXP=
+    )
+
+  source_group(ThirdParty\\Libpng REGULAR_EXPRESSION ${LIBPNG_SOURCES_DIR}/.*)
+
+else()
+  include(FindPNG)
+
+  if (NOT ${PNG_FOUND})
+    message(FATAL_ERROR "Unable to find LibPNG")
+  endif()
+
+  include_directories(${PNG_INCLUDE_DIRS})
+  link_libraries(${PNG_LIBRARIES})
+  add_definitions(${PNG_DEFINITIONS})
+endif()
--- a/Plugin/Configuration.cpp	Sun Aug 02 09:34:56 2015 +0200
+++ b/Plugin/Configuration.cpp	Sun Aug 02 11:14:19 2015 +0200
@@ -128,10 +128,20 @@
 
   bool RestApiGetString(std::string& result,
                         OrthancPluginContext* context,
-                        const std::string& uri)
+                        const std::string& uri,
+                        bool applyPlugins)
   {
     OrthancPluginMemoryBuffer buffer;
-    int code = OrthancPluginRestApiGet(context, &buffer, uri.c_str());
+    int code;
+    if (applyPlugins)
+    {
+      code = OrthancPluginRestApiGetAfterPlugins(context, &buffer, uri.c_str());
+    }
+    else
+    {
+      code = OrthancPluginRestApiGet(context, &buffer, uri.c_str());
+    }
+
     if (code)
     {
       // Error
@@ -242,20 +252,11 @@
 
     std::string GetRoot(const Json::Value& configuration)
     {
-      std::string root;
-
-      if (configuration.isMember("DicomWeb"))
-      {
-        root = GetStringValue(configuration["DicomWeb"], "Root", "");
-      }
-
-      if (root.empty())
-      {
-        root = "/dicom-web/";
-      }
+      std::string root = GetStringValue(configuration, "Root", "/dicom-web/");
 
       // Make sure the root URI starts and ends with a slash
-      if (root[0] != '/')
+      if (root.size() == 0 ||
+          root[0] != '/')
       {
         root = "/" + root;
       }
@@ -269,17 +270,32 @@
     }
 
 
+    std::string GetWadoRoot(const Json::Value& configuration)
+    {
+      std::string root = GetStringValue(configuration, "WadoRoot", "/wado/");
+
+      // Make sure the root URI starts with a slash
+      if (root.size() == 0 ||
+          root[0] != '/')
+      {
+        root = "/" + root;
+      }
+
+      // Remove the trailing slash, if any
+      if (root[root.length() - 1] == '/')
+      {
+        root = root.substr(0, root.length() - 1);
+      }
+
+      return root;
+    }
+
+
     std::string  GetBaseUrl(const Json::Value& configuration,
                             const OrthancPluginHttpRequest* request)
     {
-      std::string host;
-      bool ssl = false;
-
-      if (configuration.isMember("DicomWeb"))
-      {
-        host = GetStringValue(configuration["DicomWeb"], "Host", "");
-        ssl = GetBoolValue(configuration["DicomWeb"], "Ssl", false);
-      }
+      std::string host = GetStringValue(configuration, "Host", "");
+      bool ssl = GetBoolValue(configuration, "Ssl", false);
 
       if (host.empty() &&
           !LookupHttpHeader(host, request, "host"))
--- a/Plugin/Configuration.h	Sun Aug 02 09:34:56 2015 +0200
+++ b/Plugin/Configuration.h	Sun Aug 02 11:14:19 2015 +0200
@@ -47,7 +47,8 @@
 
   bool RestApiGetString(std::string& result,
                         OrthancPluginContext* context,
-                        const std::string& uri);
+                        const std::string& uri,
+                        bool applyPlugins = false);
 
   bool RestApiGetJson(Json::Value& result,
                       OrthancPluginContext* context,
@@ -68,6 +69,8 @@
 
     std::string GetRoot(const Json::Value& configuration);
 
+    std::string GetWadoRoot(const Json::Value& configuration);
+      
     std::string GetBaseUrl(const Json::Value& configuration,
                            const OrthancPluginHttpRequest* request);
   }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugin/JpegWriter.cpp	Sun Aug 02 11:14:19 2015 +0200
@@ -0,0 +1,246 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 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 Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "JpegWriter.h"
+
+#include "../Orthanc/Core/OrthancException.h"
+
+#include <jpeglib.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <vector>
+#include <string.h>
+#include <stdlib.h>
+
+namespace OrthancPlugins
+{
+  namespace
+  {
+    class ErrorManager 
+    {
+    private:
+      struct jpeg_error_mgr pub;  /* "public" fields */
+      jmp_buf setjmp_buffer;      /* for return to caller */
+      std::string message;
+
+      static void OutputMessage(j_common_ptr cinfo)
+      {
+        char message[JMSG_LENGTH_MAX];
+        (*cinfo->err->format_message) (cinfo, message);
+
+        ErrorManager* that = reinterpret_cast<ErrorManager*>(cinfo->err);
+        that->message = std::string(message);
+      }
+
+
+      static void ErrorExit(j_common_ptr cinfo)
+      {
+        (*cinfo->err->output_message) (cinfo);
+
+        ErrorManager* that = reinterpret_cast<ErrorManager*>(cinfo->err);
+        longjmp(that->setjmp_buffer, 1);
+      }
+      
+
+    public:
+      ErrorManager()
+      {
+        memset(&pub, 0, sizeof(struct jpeg_error_mgr));
+        memset(&setjmp_buffer, 0, sizeof(jmp_buf));
+
+        jpeg_std_error(&pub);
+        pub.error_exit = ErrorExit;
+        pub.output_message = OutputMessage;
+      }
+
+      struct jpeg_error_mgr* GetPublic()
+      {
+        return &pub;
+      }
+
+      jmp_buf& GetJumpBuffer()
+      {
+        return setjmp_buffer;
+      }
+
+      const std::string& GetMessage() const
+      {
+        return message;
+      }
+    };
+  }
+
+
+  static void GetLines(std::vector<uint8_t*>& lines,
+                       unsigned int height,
+                       unsigned int pitch,
+                       Orthanc::PixelFormat format,
+                       const void* buffer)
+  {
+    if (format != Orthanc::PixelFormat_Grayscale8 &&
+        format != Orthanc::PixelFormat_RGB24)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+
+    lines.resize(height);
+
+    uint8_t* base = const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(buffer));
+    for (unsigned int y = 0; y < height; y++)
+    {
+      lines[y] = base + static_cast<intptr_t>(y) * static_cast<intptr_t>(pitch);
+    }
+  }
+
+
+  static void Compress(struct jpeg_compress_struct& cinfo,
+                       std::vector<uint8_t*>& lines,
+                       unsigned int width,
+                       unsigned int height,
+                       Orthanc::PixelFormat format,
+                       int quality)
+  {
+    cinfo.image_width = width;
+    cinfo.image_height = height;
+
+    switch (format)
+    {
+      case Orthanc::PixelFormat_Grayscale8:
+        cinfo.input_components = 1;
+        cinfo.in_color_space = JCS_GRAYSCALE;
+        break;
+
+      case Orthanc::PixelFormat_RGB24:
+        cinfo.input_components = 3;
+        cinfo.in_color_space = JCS_RGB;
+        break;
+
+      default:
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+    }
+
+    jpeg_set_defaults(&cinfo);
+    jpeg_set_quality(&cinfo, quality, TRUE);
+    jpeg_start_compress(&cinfo, TRUE);
+    jpeg_write_scanlines(&cinfo, &lines[0], height);
+    jpeg_finish_compress(&cinfo);
+    jpeg_destroy_compress(&cinfo);
+  }
+                       
+
+  void JpegWriter::SetQuality(uint8_t quality)
+  {
+    if (quality <= 0 || quality > 100)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+
+    quality_ = quality;
+  }
+
+
+  void JpegWriter::WriteToFile(const char* filename,
+                               unsigned int width,
+                               unsigned int height,
+                               unsigned int pitch,
+                               Orthanc::PixelFormat format,
+                               const void* buffer)
+  {
+    FILE* fp = fopen(filename, "wb");
+    if (fp == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_FullStorage);
+    }
+
+    std::vector<uint8_t*> lines;
+    GetLines(lines, height, pitch, format, buffer);
+
+    struct jpeg_compress_struct cinfo;
+    memset(&cinfo, 0, sizeof(struct jpeg_compress_struct));
+
+    ErrorManager jerr;
+    cinfo.err = jerr.GetPublic();
+
+    if (setjmp(jerr.GetJumpBuffer())) 
+    {
+      /* If we get here, the JPEG code has signaled an error.
+       * We need to clean up the JPEG object, close the input file, and return.
+       */
+      jpeg_destroy_compress(&cinfo);
+      fclose(fp);
+      throw Orthanc::OrthancException("Error during JPEG encoding: " + jerr.GetMessage());
+    }
+
+    // Do not allocate data on the stack below this line!
+
+    jpeg_create_compress(&cinfo);
+    jpeg_stdio_dest(&cinfo, fp);
+    Compress(cinfo, lines, width, height, format, quality_);
+
+    // Everything went fine, "setjmp()" didn't get called
+
+    fclose(fp);
+  }
+
+
+  void JpegWriter::WriteToMemory(std::string& jpeg,
+                                 unsigned int width,
+                                 unsigned int height,
+                                 unsigned int pitch,
+                                 Orthanc::PixelFormat format,
+                                 const void* buffer)
+  {
+    std::vector<uint8_t*> lines;
+    GetLines(lines, height, pitch, format, buffer);
+
+    struct jpeg_compress_struct cinfo;
+    memset(&cinfo, 0, sizeof(struct jpeg_compress_struct));
+
+    ErrorManager jerr;
+
+    unsigned char* data = NULL;
+    unsigned long size;
+
+    if (setjmp(jerr.GetJumpBuffer())) 
+    {
+      jpeg_destroy_compress(&cinfo);
+
+      if (data != NULL)
+      {
+        free(data);
+      }
+
+      throw Orthanc::OrthancException("Error during JPEG encoding: " + jerr.GetMessage());
+    }
+
+    // Do not allocate data on the stack below this line!
+
+    jpeg_create_compress(&cinfo);
+    cinfo.err = jerr.GetPublic();
+    jpeg_mem_dest(&cinfo, &data, &size);
+
+    Compress(cinfo, lines, width, height, format, quality_);
+
+    // Everything went fine, "setjmp()" didn't get called
+
+    jpeg.assign(reinterpret_cast<const char*>(data), size);
+    free(data);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugin/JpegWriter.h	Sun Aug 02 11:14:19 2015 +0200
@@ -0,0 +1,75 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 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 Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../Orthanc/Core/ImageFormats/ImageAccessor.h"
+
+#include <string>
+#include <stdint.h>
+
+namespace OrthancPlugins
+{
+  class JpegWriter
+  {
+  private:
+    int  quality_;
+
+  public:
+    JpegWriter() : quality_(90)
+    {
+    }
+
+    void SetQuality(uint8_t quality);
+
+    uint8_t GetQuality() const
+    {
+      return quality_;
+    }
+
+    void WriteToFile(const char* filename,
+                     unsigned int width,
+                     unsigned int height,
+                     unsigned int pitch,
+                     Orthanc::PixelFormat format,
+                     const void* buffer);
+
+    void WriteToMemory(std::string& jpeg,
+                       unsigned int width,
+                       unsigned int height,
+                       unsigned int pitch,
+                       Orthanc::PixelFormat format,
+                       const void* buffer);
+
+    void WriteToFile(const char* filename,
+                     const Orthanc::ImageAccessor& accessor)
+    {
+      WriteToFile(filename, accessor.GetWidth(), accessor.GetHeight(),
+                  accessor.GetPitch(), accessor.GetFormat(), accessor.GetConstBuffer());
+    }
+
+    void WriteToMemory(std::string& jpeg,
+                       const Orthanc::ImageAccessor& accessor)
+    {
+      WriteToMemory(jpeg, accessor.GetWidth(), accessor.GetHeight(),
+                    accessor.GetPitch(), accessor.GetFormat(), accessor.GetConstBuffer());
+    }
+  };
+}
--- a/Plugin/Plugin.cpp	Sun Aug 02 09:34:56 2015 +0200
+++ b/Plugin/Plugin.cpp	Sun Aug 02 11:14:19 2015 +0200
@@ -23,6 +23,7 @@
 #include "QidoRs.h"
 #include "StowRs.h"
 #include "WadoRs.h"
+#include "Wado.h"
 #include "Configuration.h"
 
 
@@ -120,38 +121,70 @@
     }
 
 
+    OrthancPluginSetDescription(context_, "Implementation of DICOM Web (QIDO-RS, STOW-RS and WADO-RS) and WADO.");
+
+    // Read the configuration
     dictionary_ = &gdcm::Global::GetInstance().GetDicts().GetPublicDict();
 
+    configuration_ = Json::objectValue;
 
-    if (!OrthancPlugins::Configuration::Read(configuration_, context) ||
-        configuration_.type() != Json::objectValue)
     {
-      OrthancPluginLogError(context_, "Unable to read the configuration file");
-      return -1;
+      Json::Value tmp;
+      if (!OrthancPlugins::Configuration::Read(tmp, context) ||
+          tmp.type() != Json::objectValue)
+      {
+        OrthancPluginLogError(context_, "Unable to read the configuration file");
+        return -1;
+      }
+
+      if (tmp.isMember("DicomWeb") &&
+          tmp["DicomWeb"].type() == Json::objectValue)
+      {
+        configuration_ = tmp["DicomWeb"];
+      }
     }
 
-    std::string root = OrthancPlugins::Configuration::GetRoot(configuration_);
+    // Configure the DICOMweb callbacks
+    if (OrthancPlugins::Configuration::GetBoolValue(configuration_, "Enable", true))
+    {
+      std::string root = OrthancPlugins::Configuration::GetRoot(configuration_);
 
-    {
       std::string message = "URI to the DICOMweb REST API: " + root;
       OrthancPluginLogWarning(context_, message.c_str());
+
+      Register(root, "instances", SearchForInstances);    
+      Register(root, "series", SearchForSeries);    
+      Register(root, "studies", SwitchStudies);
+      Register(root, "studies/([^/]*)", SwitchStudy);
+      Register(root, "studies/([^/]*)/instances", SearchForInstances);    
+      Register(root, "studies/([^/]*)/metadata", RetrieveStudyMetadata);
+      Register(root, "studies/([^/]*)/series", SearchForSeries);    
+      Register(root, "studies/([^/]*)/series/([^/]*)", RetrieveDicomSeries);
+      Register(root, "studies/([^/]*)/series/([^/]*)/instances", SearchForInstances);    
+      Register(root, "studies/([^/]*)/series/([^/]*)/instances/([^/]*)", RetrieveDicomInstance);
+      Register(root, "studies/([^/]*)/series/([^/]*)/instances/([^/]*)/bulk/(.*)", RetrieveBulkData);
+      Register(root, "studies/([^/]*)/series/([^/]*)/instances/([^/]*)/metadata", RetrieveInstanceMetadata);
+      Register(root, "studies/([^/]*)/series/([^/]*)/metadata", RetrieveSeriesMetadata);
+    }
+    else
+    {
+      OrthancPluginLogWarning(context_, "DICOMweb support is disabled");
     }
 
-    OrthancPluginSetDescription(context_, "Implementation of DICOM Web (QIDO-RS, STOW-RS and WADO-RS).");
+    // Configure the WADO callback
+    if (OrthancPlugins::Configuration::GetBoolValue(configuration_, "EnableWado", true))
+    {
+      std::string wado = OrthancPlugins::Configuration::GetWadoRoot(configuration_);
 
-    Register(root, "instances", SearchForInstances);    
-    Register(root, "series", SearchForSeries);    
-    Register(root, "studies", SwitchStudies);
-    Register(root, "studies/([^/]*)", SwitchStudy);
-    Register(root, "studies/([^/]*)/instances", SearchForInstances);    
-    Register(root, "studies/([^/]*)/metadata", RetrieveStudyMetadata);
-    Register(root, "studies/([^/]*)/series", SearchForSeries);    
-    Register(root, "studies/([^/]*)/series/([^/]*)", RetrieveDicomSeries);
-    Register(root, "studies/([^/]*)/series/([^/]*)/instances", SearchForInstances);    
-    Register(root, "studies/([^/]*)/series/([^/]*)/instances/([^/]*)", RetrieveDicomInstance);
-    Register(root, "studies/([^/]*)/series/([^/]*)/instances/([^/]*)/bulk/(.*)", RetrieveBulkData);
-    Register(root, "studies/([^/]*)/series/([^/]*)/instances/([^/]*)/metadata", RetrieveInstanceMetadata);
-    Register(root, "studies/([^/]*)/series/([^/]*)/metadata", RetrieveSeriesMetadata);
+      std::string message = "URI to the WADO API: " + wado;
+      OrthancPluginLogWarning(context_, message.c_str());
+
+      OrthancPluginRegisterRestCallback(context_, wado.c_str(), WadoCallback);
+    }
+    else
+    {
+      OrthancPluginLogWarning(context_, "WADO support is disabled");
+    }
 
     return 0;
   }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugin/Wado.cpp	Sun Aug 02 11:14:19 2015 +0200
@@ -0,0 +1,283 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 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 Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "Wado.h"
+#include "Plugin.h"
+
+#include "../Orthanc/Core/OrthancException.h"
+#include "../Orthanc/Core/ImageFormats/PngReader.h"
+#include "JpegWriter.h"
+#include "Configuration.h"
+
+#include <string>
+
+static bool MapWadoToOrthancIdentifier(std::string& orthanc,
+                                       char* (*func) (OrthancPluginContext*, const char*),
+                                       const std::string& dicom)
+{
+  char* tmp = func(context_, dicom.c_str());
+
+  if (tmp)
+  {
+    orthanc = tmp;
+    OrthancPluginFreeString(context_, tmp);
+    return true;
+  }
+  else
+  {
+    return false;
+  }
+}
+
+
+static bool LocateInstance(std::string& instance,
+                           std::string& contentType,
+                           const OrthancPluginHttpRequest* request)
+{
+  std::string requestType, studyUid, seriesUid, objectUid;
+
+  for (uint32_t i = 0; i < request->getCount; i++)
+  {
+    std::string key(request->getKeys[i]);
+    std::string value(request->getValues[i]);
+
+    if (key == "studyUID")
+    {
+      studyUid = value;
+    }
+    else if (key == "seriesUID")
+    {
+      seriesUid = value;
+    }
+    else if (key == "objectUID")  // In WADO, "objectUID" corresponds to "SOPInstanceUID"
+    {
+      objectUid = value;
+    }
+    else if (key == "requestType")
+    {
+      requestType = value;
+    }
+    else if (key == "contentType")
+    {
+      contentType = value;
+    }
+  }
+
+  if (requestType != "WADO")
+  {
+    std::string msg = "WADO: Invalid requestType: \"" + requestType + "\"";
+    OrthancPluginLogError(context_, msg.c_str());
+    return false;
+  }
+
+  if (objectUid.empty())
+  {
+    OrthancPluginLogError(context_, "WADO: No SOPInstanceUID provided");
+    return false;
+  }
+
+  if (!MapWadoToOrthancIdentifier(instance, OrthancPluginLookupInstance, objectUid))
+  {
+    std::string msg = "WADO: No such SOPInstanceUID in Orthanc: \"" + objectUid + "\"";
+    OrthancPluginLogError(context_, msg.c_str());
+    return false;
+  }
+
+  /**
+   * Below are only sanity checks to ensure that the possibly provided
+   * "seriesUID" and "studyUID" match that of the provided instance.
+   **/
+
+  if (!seriesUid.empty())
+  {
+    std::string series;
+    if (!MapWadoToOrthancIdentifier(series, OrthancPluginLookupSeries, seriesUid))
+    {
+      std::string msg = "WADO: No such SeriesInstanceUID in Orthanc: \"" + seriesUid + "\"";
+      OrthancPluginLogError(context_, msg.c_str());
+      return false;
+    }
+    else
+    {
+      Json::Value info;
+      if (!OrthancPlugins::RestApiGetJson(info, context_, "/instances/" + instance + "/series") ||
+          info["MainDicomTags"]["SeriesInstanceUID"] != seriesUid)
+      {
+        std::string msg = "WADO: Instance " + objectUid + " does not belong to series " + seriesUid;
+        OrthancPluginLogError(context_, msg.c_str());
+        return false;
+      }
+    }
+  }
+  
+  if (!studyUid.empty())
+  {
+    std::string study;
+    if (!MapWadoToOrthancIdentifier(study, OrthancPluginLookupStudy, studyUid))
+    {
+      std::string msg = "WADO: No such StudyInstanceUID in Orthanc: \"" + studyUid + "\"";
+      OrthancPluginLogError(context_, msg.c_str());
+      return false;
+    }
+    else
+    {
+      Json::Value info;
+      if (!OrthancPlugins::RestApiGetJson(info, context_, "/instances/" + instance + "/study") ||
+          info["MainDicomTags"]["StudyInstanceUID"] != studyUid)
+      {
+        std::string msg = "WADO: Instance " + objectUid + " does not belong to study " + studyUid;
+        OrthancPluginLogError(context_, msg.c_str());
+        return false;
+      }
+    }
+  }
+  
+  return true;
+}
+
+
+static int32_t AnswerDicom(OrthancPluginRestOutput* output,
+                           const std::string& instance)
+{
+  std::string uri = "/instances/" + instance + "/file";
+
+  std::string dicom;
+  if (OrthancPlugins::RestApiGetString(dicom, context_, uri))
+  {
+    OrthancPluginAnswerBuffer(context_, output, dicom.c_str(), dicom.size(), "application/dicom");
+    return 0;
+  }
+  else
+  {
+    std::string msg = "WADO: Unable to retrieve DICOM file from " + uri;
+    OrthancPluginLogError(context_, msg.c_str());
+    return -1;
+  }
+}
+
+
+static bool RetrievePngPreview(std::string& png,
+                               const std::string& instance)
+{
+  std::string uri = "/instances/" + instance + "/preview";
+
+  if (OrthancPlugins::RestApiGetString(png, context_, uri))
+  {
+    return true;
+  }
+  else
+  {
+    std::string msg = "WADO: Unable to generate a preview image for " + uri;
+    OrthancPluginLogError(context_, msg.c_str());
+    return false;
+  }
+}
+
+
+static int32_t AnswerPngPreview(OrthancPluginRestOutput* output,
+                                const std::string& instance)
+{
+  std::string png;
+  if (!RetrievePngPreview(png, instance))
+  {
+    return -1;
+  }
+
+  OrthancPluginAnswerBuffer(context_, output, png.c_str(), png.size(), "image/png");
+  return 0;
+}
+
+
+static int32_t AnswerJpegPreview(OrthancPluginRestOutput* output,
+                                 const std::string& instance)
+{
+  // Retrieve the preview in the PNG format
+  std::string png;
+  if (!RetrievePngPreview(png, instance))
+  {
+    return -1;
+  }
+
+  // Decode the PNG file
+  Orthanc::PngReader reader;
+  reader.ReadFromMemory(png);
+
+  // Convert to JPEG
+  OrthancPlugins::JpegWriter writer;
+  std::string jpeg;
+  writer.WriteToMemory(jpeg, reader);
+
+  OrthancPluginAnswerBuffer(context_, output, jpeg.c_str(), jpeg.size(), "image/jpeg");
+  return 0;
+}
+
+
+int32_t WadoCallback(OrthancPluginRestOutput* output,
+                     const char* url,
+                     const OrthancPluginHttpRequest* request)
+{
+  try
+  {
+    if (request->method != OrthancPluginHttpMethod_Get)
+    {
+      OrthancPluginSendMethodNotAllowed(context_, output, "GET");
+      return -1;
+    }
+
+    std::string instance;
+    std::string contentType = "image/jpg";  // By default, JPEG image will be returned
+    if (!LocateInstance(instance, contentType, request))
+    {
+      return -1;
+    }
+
+    if (contentType == "application/dicom")
+    {
+      return AnswerDicom(output, instance);
+    }
+    else if (contentType == "image/png")
+    {
+      return AnswerPngPreview(output, instance);
+    }
+    else if (contentType == "image/jpeg" ||
+             contentType == "image/jpg")
+    {
+      return AnswerJpegPreview(output, instance);
+    }
+    else
+    {
+      std::string msg = "WADO: Unsupported content type: \"" + contentType + "\"";
+      OrthancPluginLogError(context_, msg.c_str());
+      return -1;
+    }
+
+    return 0;
+  }
+  catch (Orthanc::OrthancException& e)
+  {
+    OrthancPluginLogError(context_, e.What());
+    return -1;
+  }
+  catch (std::runtime_error& e)
+  {
+    OrthancPluginLogError(context_, e.what());
+    return -1;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugin/Wado.h	Sun Aug 02 11:14:19 2015 +0200
@@ -0,0 +1,27 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 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 Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include <orthanc/OrthancCPlugin.h>
+
+int32_t WadoCallback(OrthancPluginRestOutput* output,
+                     const char* url,
+                     const OrthancPluginHttpRequest* request);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/CMake/LibJpegConfiguration.cmake	Sun Aug 02 11:14:19 2015 +0200
@@ -0,0 +1,104 @@
+# Orthanc - A Lightweight, RESTful DICOM Store
+# Copyright (C) 2012-2015 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 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_LIBJPEG)
+  set(LIBJPEG_SOURCES_DIR ${CMAKE_BINARY_DIR}/jpeg-9a)
+  DownloadPackage(
+    "3353992aecaee1805ef4109aadd433e7"
+    "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/jpegsrc.v9a.tar.gz"
+    "${LIBJPEG_SOURCES_DIR}")
+
+  include_directories(
+    ${LIBJPEG_SOURCES_DIR}/
+    )
+
+  list(APPEND LIBJPEG_SOURCES 
+    ${LIBJPEG_SOURCES_DIR}/jaricom.c
+    ${LIBJPEG_SOURCES_DIR}/jcapimin.c
+    ${LIBJPEG_SOURCES_DIR}/jcapistd.c
+    ${LIBJPEG_SOURCES_DIR}/jcarith.c
+    ${LIBJPEG_SOURCES_DIR}/jccoefct.c
+    ${LIBJPEG_SOURCES_DIR}/jccolor.c
+    ${LIBJPEG_SOURCES_DIR}/jcdctmgr.c
+    ${LIBJPEG_SOURCES_DIR}/jchuff.c
+    ${LIBJPEG_SOURCES_DIR}/jcinit.c
+    ${LIBJPEG_SOURCES_DIR}/jcmarker.c
+    ${LIBJPEG_SOURCES_DIR}/jcmaster.c
+    ${LIBJPEG_SOURCES_DIR}/jcomapi.c
+    ${LIBJPEG_SOURCES_DIR}/jcparam.c
+    ${LIBJPEG_SOURCES_DIR}/jcprepct.c
+    ${LIBJPEG_SOURCES_DIR}/jcsample.c
+    ${LIBJPEG_SOURCES_DIR}/jctrans.c
+    ${LIBJPEG_SOURCES_DIR}/jdapimin.c
+    ${LIBJPEG_SOURCES_DIR}/jdapistd.c
+    ${LIBJPEG_SOURCES_DIR}/jdarith.c
+    ${LIBJPEG_SOURCES_DIR}/jdatadst.c
+    ${LIBJPEG_SOURCES_DIR}/jdatasrc.c
+    ${LIBJPEG_SOURCES_DIR}/jdcoefct.c
+    ${LIBJPEG_SOURCES_DIR}/jdcolor.c
+    ${LIBJPEG_SOURCES_DIR}/jddctmgr.c
+    ${LIBJPEG_SOURCES_DIR}/jdhuff.c
+    ${LIBJPEG_SOURCES_DIR}/jdinput.c
+    ${LIBJPEG_SOURCES_DIR}/jcmainct.c
+    ${LIBJPEG_SOURCES_DIR}/jdmainct.c
+    ${LIBJPEG_SOURCES_DIR}/jdmarker.c
+    ${LIBJPEG_SOURCES_DIR}/jdmaster.c
+    ${LIBJPEG_SOURCES_DIR}/jdmerge.c
+    ${LIBJPEG_SOURCES_DIR}/jdpostct.c
+    ${LIBJPEG_SOURCES_DIR}/jdsample.c
+    ${LIBJPEG_SOURCES_DIR}/jdtrans.c
+    ${LIBJPEG_SOURCES_DIR}/jerror.c
+    ${LIBJPEG_SOURCES_DIR}/jfdctflt.c
+    ${LIBJPEG_SOURCES_DIR}/jfdctfst.c
+    ${LIBJPEG_SOURCES_DIR}/jfdctint.c
+    ${LIBJPEG_SOURCES_DIR}/jidctflt.c
+    ${LIBJPEG_SOURCES_DIR}/jidctfst.c
+    ${LIBJPEG_SOURCES_DIR}/jidctint.c
+    #${LIBJPEG_SOURCES_DIR}/jmemansi.c
+    #${LIBJPEG_SOURCES_DIR}/jmemdos.c
+    #${LIBJPEG_SOURCES_DIR}/jmemmac.c
+    ${LIBJPEG_SOURCES_DIR}/jmemmgr.c
+    #${LIBJPEG_SOURCES_DIR}/jmemname.c
+    ${LIBJPEG_SOURCES_DIR}/jmemnobs.c
+    ${LIBJPEG_SOURCES_DIR}/jquant1.c
+    ${LIBJPEG_SOURCES_DIR}/jquant2.c
+    ${LIBJPEG_SOURCES_DIR}/jutils.c
+
+    # ${LIBJPEG_SOURCES_DIR}/rdbmp.c
+    # ${LIBJPEG_SOURCES_DIR}/rdcolmap.c
+    # ${LIBJPEG_SOURCES_DIR}/rdgif.c
+    # ${LIBJPEG_SOURCES_DIR}/rdppm.c
+    # ${LIBJPEG_SOURCES_DIR}/rdrle.c
+    # ${LIBJPEG_SOURCES_DIR}/rdswitch.c
+    # ${LIBJPEG_SOURCES_DIR}/rdtarga.c
+    # ${LIBJPEG_SOURCES_DIR}/transupp.c
+    # ${LIBJPEG_SOURCES_DIR}/wrbmp.c
+    # ${LIBJPEG_SOURCES_DIR}/wrgif.c
+    # ${LIBJPEG_SOURCES_DIR}/wrppm.c
+    # ${LIBJPEG_SOURCES_DIR}/wrrle.c
+    # ${LIBJPEG_SOURCES_DIR}/wrtarga.c
+    )
+
+  configure_file(
+    ${LIBJPEG_SOURCES_DIR}/jconfig.txt
+    ${LIBJPEG_SOURCES_DIR}/jconfig.h COPYONLY
+    )
+
+else()
+  link_libraries(jpeg)
+endif()
--- a/Resources/SyncOrthancFolder.py	Sun Aug 02 09:34:56 2015 +0200
+++ b/Resources/SyncOrthancFolder.py	Sun Aug 02 11:14:19 2015 +0200
@@ -20,6 +20,12 @@
     'Core/ChunkedBuffer.h',
     'Core/Enumerations.cpp',
     'Core/Enumerations.h',
+    'Core/ImageFormats/ImageAccessor.cpp',
+    'Core/ImageFormats/ImageAccessor.h',
+    'Core/ImageFormats/ImageBuffer.cpp',
+    'Core/ImageFormats/ImageBuffer.h',
+    'Core/ImageFormats/PngReader.cpp',
+    'Core/ImageFormats/PngReader.h',
     'Core/OrthancException.cpp',
     'Core/OrthancException.h',
     'Core/PrecompiledHeaders.h',
@@ -31,6 +37,7 @@
     'Resources/CMake/DownloadPackage.cmake',
     'Resources/CMake/GoogleTestConfiguration.cmake',
     'Resources/CMake/JsonCppConfiguration.cmake',
+    'Resources/CMake/LibPngConfiguration.cmake',
     'Resources/CMake/PugixmlConfiguration.cmake',
     'Resources/MinGW-W64-Toolchain32.cmake',
     'Resources/MinGW-W64-Toolchain64.cmake',