changeset 945:427a1f996b7b templating

integration mainline -> templating
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 25 Jun 2014 11:56:48 +0200
parents aabc3b430890 (current diff) b3f6fb1130cd (diff)
children 226cfef3822e
files CMakeLists.txt
diffstat 61 files changed, 2806 insertions(+), 715 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Mon Jun 02 13:24:44 2014 +0200
+++ b/CMakeLists.txt	Wed Jun 25 11:56:48 2014 +0200
@@ -18,6 +18,8 @@
 SET(DCMTK_DICTIONARY_DIR "" CACHE PATH "Directory containing the DCMTK dictionaries \"dicom.dic\" and \"private.dic\" (only when using system version of DCMTK)") 
 SET(ALLOW_DOWNLOADS OFF CACHE BOOL "Allow CMake to download packages")
 SET(UNIT_TESTS_WITH_HTTP_CONNEXIONS ON CACHE BOOL "Allow unit tests to make HTTP requests")
+SET(ENABLE_JPEG ON CACHE BOOL "Enable JPEG decompression")
+SET(ENABLE_JPEG_LOSSLESS ON CACHE BOOL "Enable JPEG-LS (Lossless) decompression")
 
 # Advanced parameters to fine-tune linking against system libraries
 SET(USE_SYSTEM_JSONCPP ON CACHE BOOL "Use the system version of JsonCpp")
@@ -66,6 +68,7 @@
   Core/DicomFormat/DicomArray.cpp
   Core/DicomFormat/DicomMap.cpp
   Core/DicomFormat/DicomTag.cpp
+  Core/DicomFormat/DicomImageInformation.cpp
   Core/DicomFormat/DicomIntegerPixelAccessor.cpp
   Core/DicomFormat/DicomInstanceHasher.cpp
   Core/Enumerations.cpp
@@ -92,6 +95,7 @@
   Core/MultiThreading/ThreadedCommandProcessor.cpp
   Core/ImageFormats/ImageAccessor.cpp
   Core/ImageFormats/ImageBuffer.cpp
+  Core/ImageFormats/ImageProcessing.cpp
   Core/ImageFormats/PngReader.cpp
   Core/ImageFormats/PngWriter.cpp
   Core/SQLite/Connection.cpp
@@ -126,6 +130,7 @@
   OrthancServer/Internals/FindScp.cpp
   OrthancServer/Internals/MoveScp.cpp
   OrthancServer/Internals/StoreScp.cpp
+  OrthancServer/Internals/DicomImageDecoder.cpp
   OrthancServer/OrthancInitialization.cpp
   OrthancServer/OrthancPeerParameters.cpp
   OrthancServer/OrthancRestApi/OrthancRestAnonymizeModify.cpp
@@ -161,6 +166,9 @@
   UnitTestsSources/Lua.cpp
   UnitTestsSources/MultiThreading.cpp
   UnitTestsSources/UnitTestsMain.cpp
+  UnitTestsSources/ImageProcessingTests.cpp
+  UnitTestsSources/JpegLossless.cpp
+
   UnitTestsSources/Plustache.cpp
   )
 
@@ -203,6 +211,20 @@
 endif()
 
 
+if (ENABLE_JPEG)
+  add_definitions(-DORTHANC_JPEG_ENABLED=1)
+else()
+  add_definitions(-DORTHANC_JPEG_ENABLED=0)
+endif()
+
+
+if (ENABLE_JPEG_LOSSLESS)
+  add_definitions(-DORTHANC_JPEG_LOSSLESS_ENABLED=1)
+else()
+  add_definitions(-DORTHANC_JPEG_LOSSLESS_ENABLED=0)
+endif()
+
+
 
 #####################################################################
 ## Autogeneration of files
@@ -290,7 +312,7 @@
   OrthancServer/main.cpp
   )
 
-target_link_libraries(Orthanc ServerLibrary CoreLibrary)
+target_link_libraries(Orthanc ServerLibrary CoreLibrary ${STATIC_LUA} ${STATIC_GOOGLE_LOG})
 
 if (${OPENSSL_SOURCES_LENGTH} GREATER 0)
   target_link_libraries(Orthanc OpenSSL)
@@ -319,7 +341,7 @@
   ${GTEST_SOURCES}
   ${ORTHANC_UNIT_TESTS_SOURCES}
   )
-target_link_libraries(UnitTests ServerLibrary CoreLibrary)
+target_link_libraries(UnitTests ServerLibrary CoreLibrary ${STATIC_LUA} ${STATIC_GOOGLE_LOG})
 
 if (${OPENSSL_SOURCES_LENGTH} GREATER 0)
   target_link_libraries(UnitTests OpenSSL)
@@ -366,14 +388,17 @@
 
   add_library(OrthancClient SHARED
     ${ORTHANC_ROOT}/OrthancCppClient/OrthancCppClient.cpp
+    ${ORTHANC_ROOT}/OrthancCppClient/SharedLibrary/SharedLibrary.cpp
     ${ORTHANC_ROOT}/Resources/md5/md5.c
     ${ORTHANC_ROOT}/Resources/base64/base64.cpp
     ${ORTHANC_CPP_CLIENT_AUX}
     ${THIRD_PARTY_SOURCES}
     ${CURL_SOURCES}
+    ${GOOGLE_LOG_SOURCES}
     )
 
-  if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
+  if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR
+      ${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD")
     set_target_properties(OrthancClient
       PROPERTIES LINK_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined -Wl,--as-needed -Wl,--version-script=${ORTHANC_ROOT}/OrthancCppClient/SharedLibrary/Laaw/VersionScript.map"
       )
@@ -388,6 +413,10 @@
         )
     endif()
 
+  elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
+    # TODO
+    target_link_libraries(OrthancClient pthread)
+
   else()
     message(FATAL_ERROR "Support your platform here")
   endif()
--- a/Core/ChunkedBuffer.cpp	Mon Jun 02 13:24:44 2014 +0200
+++ b/Core/ChunkedBuffer.cpp	Wed Jun 25 11:56:48 2014 +0200
@@ -65,6 +65,15 @@
   }
 
 
+  void ChunkedBuffer::AddChunk(const std::string& chunk)
+  {
+    if (chunk.size() > 0)
+    {
+      AddChunk(&chunk[0], chunk.size());
+    }
+  }
+
+
   void ChunkedBuffer::Flatten(std::string& result)
   {
     result.resize(numBytes_);
--- a/Core/ChunkedBuffer.h	Mon Jun 02 13:24:44 2014 +0200
+++ b/Core/ChunkedBuffer.h	Wed Jun 25 11:56:48 2014 +0200
@@ -64,6 +64,8 @@
     void AddChunk(const char* chunkData,
                   size_t chunkSize);
 
+    void AddChunk(const std::string& chunk);
+
     void Flatten(std::string& result);
   };
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/DicomFormat/DicomImageInformation.cpp	Wed Jun 25 11:56:48 2014 +0200
@@ -0,0 +1,188 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2014 Medical Physics Department, CHU 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"
+
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+
+#include "DicomImageInformation.h"
+
+#include "../OrthancException.h"
+#include <boost/lexical_cast.hpp>
+#include <limits>
+#include <cassert>
+#include <stdio.h>
+
+namespace Orthanc
+{
+  DicomImageInformation::DicomImageInformation(const DicomMap& values)
+  {
+    unsigned int pixelRepresentation;
+    unsigned int planarConfiguration = 0;
+
+    try
+    {
+      width_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_COLUMNS).AsString());
+      height_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_ROWS).AsString());
+      bitsAllocated_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_BITS_ALLOCATED).AsString());
+
+      try
+      {
+        samplesPerPixel_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_SAMPLES_PER_PIXEL).AsString());
+      }
+      catch (OrthancException&)
+      {
+        samplesPerPixel_ = 1;  // Assume 1 color channel
+      }
+
+      try
+      {
+        bitsStored_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_BITS_STORED).AsString());
+      }
+      catch (OrthancException&)
+      {
+        bitsStored_ = bitsAllocated_;
+      }
+
+      try
+      {
+        highBit_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_HIGH_BIT).AsString());
+      }
+      catch (OrthancException&)
+      {
+        highBit_ = bitsStored_ - 1;
+      }
+
+      try
+      {
+        pixelRepresentation = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_PIXEL_REPRESENTATION).AsString());
+      }
+      catch (OrthancException&)
+      {
+        pixelRepresentation = 0;  // Assume unsigned pixels
+      }
+
+      if (samplesPerPixel_ > 1)
+      {
+        // The "Planar Configuration" is only set when "Samples per Pixels" is greater than 1
+        // https://www.dabsoft.ch/dicom/3/C.7.6.3.1.3/
+        try
+        {
+          planarConfiguration = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_PLANAR_CONFIGURATION).AsString());
+        }
+        catch (OrthancException&)
+        {
+          planarConfiguration = 0;  // Assume interleaved color channels
+        }
+      }
+    }
+    catch (boost::bad_lexical_cast&)
+    {
+      throw OrthancException(ErrorCode_NotImplemented);
+    }
+    catch (OrthancException&)
+    {
+      throw OrthancException(ErrorCode_NotImplemented);
+    }
+
+    try
+    {
+      numberOfFrames_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_NUMBER_OF_FRAMES).AsString());
+    }
+    catch (OrthancException)
+    {
+      // If the tag "NumberOfFrames" is absent, assume there is a single frame
+      numberOfFrames_ = 1;
+    }
+    catch (boost::bad_lexical_cast&)
+    {
+      throw OrthancException(ErrorCode_NotImplemented);
+    }
+
+    if ((bitsAllocated_ != 8 && bitsAllocated_ != 16 && 
+         bitsAllocated_ != 24 && bitsAllocated_ != 32) ||
+        numberOfFrames_ == 0 ||
+        (planarConfiguration != 0 && planarConfiguration != 1))
+    {
+      throw OrthancException(ErrorCode_NotImplemented);
+    }
+
+    if (bitsAllocated_ > 32 ||
+        bitsStored_ >= 32)
+    {
+      // Not available, as the accessor internally uses int32_t values
+      throw OrthancException(ErrorCode_NotImplemented);
+    }
+
+    if (samplesPerPixel_ == 0)
+    {
+      throw OrthancException(ErrorCode_NotImplemented);
+    }
+
+    bytesPerValue_ = bitsAllocated_ / 8;
+
+    isPlanar_ = (planarConfiguration != 0 ? true : false);
+    isSigned_ = (pixelRepresentation != 0 ? true : false);
+  }
+
+
+  bool DicomImageInformation::ExtractPixelFormat(PixelFormat& format) const
+  {
+    if (GetBitsStored() == 8 && GetChannelCount() == 1 && !IsSigned())
+    {
+      format = PixelFormat_Grayscale8;
+      return true;
+    }
+
+    if (GetBitsStored() == 8 && GetChannelCount() == 3 && !IsSigned())
+    {
+      format = PixelFormat_RGB24;
+      return true;
+    }
+
+    if (GetBitsAllocated() == 16 && GetChannelCount() == 1 && !IsSigned())
+    {
+      format = PixelFormat_Grayscale16;
+      return true;
+    }
+
+    if (GetBitsAllocated() == 16 && GetChannelCount() == 1 && IsSigned())
+    {
+      format = PixelFormat_SignedGrayscale16;
+      return true;
+    }
+
+    return false;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/DicomFormat/DicomImageInformation.h	Wed Jun 25 11:56:48 2014 +0200
@@ -0,0 +1,117 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2014 Medical Physics Department, CHU 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 "DicomMap.h"
+
+#include <stdint.h>
+
+namespace Orthanc
+{
+  class DicomImageInformation
+  {
+  private:
+    unsigned int width_;
+    unsigned int height_;
+    unsigned int samplesPerPixel_;
+    unsigned int numberOfFrames_;
+
+    bool isPlanar_;
+    bool isSigned_;
+    size_t bytesPerValue_;
+
+    unsigned int bitsAllocated_;
+    unsigned int bitsStored_;
+    unsigned int highBit_;
+
+  public:
+    DicomImageInformation(const DicomMap& values);
+
+    unsigned int GetWidth() const
+    {
+      return width_;
+    }
+
+    unsigned int GetHeight() const
+    {
+      return height_;
+    }
+
+    unsigned int GetNumberOfFrames() const
+    {
+      return numberOfFrames_;
+    }
+
+    unsigned int GetChannelCount() const
+    {
+      return samplesPerPixel_;
+    }
+
+    unsigned int GetBitsStored() const
+    {
+      return bitsStored_;
+    }
+
+    size_t GetBytesPerValue() const
+    {
+      return bytesPerValue_;
+    }
+
+    bool IsSigned() const
+    {
+      return isSigned_;
+    }
+
+    unsigned int GetBitsAllocated() const
+    {
+      return bitsAllocated_;
+    }
+
+    unsigned int GetHighBit() const
+    {
+      return highBit_;
+    }
+
+    bool IsPlanar() const
+    {
+      return isPlanar_;
+    }
+
+    unsigned int GetShift() const
+    {
+      return highBit_ + 1 - bitsStored_;
+    }
+
+    bool ExtractPixelFormat(PixelFormat& format) const;
+  };
+}
--- a/Core/DicomFormat/DicomIntegerPixelAccessor.cpp	Mon Jun 02 13:24:44 2014 +0200
+++ b/Core/DicomFormat/DicomIntegerPixelAccessor.cpp	Wed Jun 25 11:56:48 2014 +0200
@@ -49,102 +49,42 @@
   DicomIntegerPixelAccessor::DicomIntegerPixelAccessor(const DicomMap& values,
                                                        const void* pixelData,
                                                        size_t size) :
+    information_(values),
     pixelData_(pixelData),
     size_(size)
   {
-    unsigned int bitsAllocated;
-    unsigned int bitsStored;
-    unsigned int highBit;
-    unsigned int pixelRepresentation;
-    planarConfiguration_ = 0;
-
-    try
-    {
-      width_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_COLUMNS).AsString());
-      height_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_ROWS).AsString());
-      samplesPerPixel_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_SAMPLES_PER_PIXEL).AsString());
-      bitsAllocated = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_BITS_ALLOCATED).AsString());
-      bitsStored = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_BITS_STORED).AsString());
-      highBit = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_HIGH_BIT).AsString());
-      pixelRepresentation = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_PIXEL_REPRESENTATION).AsString());
-
-      if (samplesPerPixel_ > 1)
-      {
-        // The "Planar Configuration" is only set when "Samples per Pixels" is greater than 1
-        // https://www.dabsoft.ch/dicom/3/C.7.6.3.1.3/
-        planarConfiguration_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_PLANAR_CONFIGURATION).AsString());
-      }
-    }
-    catch (boost::bad_lexical_cast)
-    {
-      throw OrthancException(ErrorCode_NotImplemented);
-    }
-    catch (OrthancException)
-    {
-      throw OrthancException(ErrorCode_NotImplemented);
-    }
+    frame_ = 0;
+    frameOffset_ = (information_.GetHeight() * information_.GetWidth() * 
+                    information_.GetBytesPerValue() * information_.GetChannelCount());
 
-    frame_ = 0;
-    try
-    {
-      numberOfFrames_ = boost::lexical_cast<unsigned int>(values.GetValue(DICOM_TAG_NUMBER_OF_FRAMES).AsString());
-    }
-    catch (OrthancException)
-    {
-      // If the tag "NumberOfFrames" is absent, assume there is a single frame
-      numberOfFrames_ = 1;
-    }
-    catch (boost::bad_lexical_cast)
-    {
-      throw OrthancException(ErrorCode_NotImplemented);
-    }
-
-    if ((bitsAllocated != 8 && bitsAllocated != 16 && 
-         bitsAllocated != 24 && bitsAllocated != 32) ||
-        numberOfFrames_ == 0 ||
-        (planarConfiguration_ != 0 && planarConfiguration_ != 1))
-    {
-      throw OrthancException(ErrorCode_NotImplemented);
-    }
-
-    if (bitsAllocated > 32 ||
-        bitsStored >= 32)
-    {
-      // Not available, as the accessor internally uses int32_t values
-      throw OrthancException(ErrorCode_NotImplemented);
-    }
-
-    if (samplesPerPixel_ == 0)
-    {
-      throw OrthancException(ErrorCode_NotImplemented);
-    }
-
-    bytesPerPixel_ = bitsAllocated / 8;
-    shift_ = highBit + 1 - bitsStored;
-    frameOffset_ = height_ * width_ * bytesPerPixel_ * samplesPerPixel_;
-
-    if (numberOfFrames_ * frameOffset_ > size)
+    if (information_.GetNumberOfFrames() * frameOffset_ > size)
     {
       throw OrthancException(ErrorCode_BadFileFormat);
     }
 
-    /*printf("%d %d %d %d %d %d %d %d\n", width_, height_, samplesPerPixel_, bitsAllocated,
-      bitsStored, highBit, pixelRepresentation, numberOfFrames_);*/
-
-    if (pixelRepresentation)
+    if (information_.IsSigned())
     {
       // Pixels are signed
-      mask_ = (1 << (bitsStored - 1)) - 1;
-      signMask_ = (1 << (bitsStored - 1));
+      mask_ = (1 << (information_.GetBitsStored() - 1)) - 1;
+      signMask_ = (1 << (information_.GetBitsStored() - 1));
     }
     else
     {
       // Pixels are unsigned
-      mask_ = (1 << bitsStored) - 1;
+      mask_ = (1 << information_.GetBitsStored()) - 1;
       signMask_ = 0;
     }
 
-    if (planarConfiguration_ == 0)
+    if (information_.IsPlanar())
+    {
+      /**
+       * Each color plane shall be sent contiguously. For RGB images,
+       * this means the order of the pixel values sent is R1, R2, R3,
+       * ..., G1, G2, G3, ..., B1, B2, B3, etc.
+       **/
+      rowOffset_ = information_.GetWidth() * information_.GetBytesPerValue();
+    }
+    else
     {
       /**
        * The sample values for the first pixel are followed by the
@@ -152,16 +92,7 @@
        * means the order of the pixel values sent shall be R1, G1, B1,
        * R2, G2, B2, ..., etc.
        **/
-      rowOffset_ = width_ * bytesPerPixel_ * samplesPerPixel_;
-    }
-    else
-    {
-      /**
-       * Each color plane shall be sent contiguously. For RGB images,
-       * this means the order of the pixel values sent is R1, R2, R3,
-       * ..., G1, G2, G3, ..., B1, B2, B3, etc.
-       **/
-      rowOffset_ = width_ * bytesPerPixel_;
+      rowOffset_ = information_.GetWidth() * information_.GetBytesPerValue() * information_.GetChannelCount();
     }
   }
 
@@ -169,7 +100,7 @@
   void DicomIntegerPixelAccessor::GetExtremeValues(int32_t& min, 
                                                    int32_t& max) const
   {
-    if (height_ == 0 || width_ == 0)
+    if (information_.GetHeight() == 0 || information_.GetWidth() == 0)
     {
       min = max = 0;
       return;
@@ -178,11 +109,11 @@
     min = std::numeric_limits<int32_t>::max();
     max = std::numeric_limits<int32_t>::min();
     
-    for (unsigned int y = 0; y < height_; y++)
+    for (unsigned int y = 0; y < information_.GetHeight(); y++)
     {
-      for (unsigned int x = 0; x < width_; x++)
+      for (unsigned int x = 0; x < information_.GetWidth(); x++)
       {
-        for (unsigned int c = 0; c < GetChannelCount(); c++)
+        for (unsigned int c = 0; c < information_.GetChannelCount(); c++)
         {
           int32_t v = GetValue(x, y);
           if (v < min)
@@ -199,13 +130,25 @@
                                               unsigned int y,
                                               unsigned int channel) const
   {
-    assert(x < width_ && y < height_ && channel < samplesPerPixel_);
+    assert(x < information_.GetWidth() && 
+           y < information_.GetHeight() && 
+           channel < information_.GetChannelCount());
     
     const uint8_t* pixel = reinterpret_cast<const uint8_t*>(pixelData_) + 
       y * rowOffset_ + frame_ * frameOffset_;
 
     // https://www.dabsoft.ch/dicom/3/C.7.6.3.1.3/
-    if (planarConfiguration_ == 0)
+    if (information_.IsPlanar())
+    {
+      /**
+       * Each color plane shall be sent contiguously. For RGB images,
+       * this means the order of the pixel values sent is R1, R2, R3,
+       * ..., G1, G2, G3, ..., B1, B2, B3, etc.
+       **/
+      assert(frameOffset_ % information_.GetChannelCount() == 0);
+      pixel += channel * frameOffset_ / information_.GetChannelCount() + x * information_.GetBytesPerValue();
+    }
+    else
     {
       /**
        * The sample values for the first pixel are followed by the
@@ -213,29 +156,19 @@
        * means the order of the pixel values sent shall be R1, G1, B1,
        * R2, G2, B2, ..., etc.
        **/
-      pixel += channel * bytesPerPixel_ + x * samplesPerPixel_ * bytesPerPixel_;
-    }
-    else
-    {
-      /**
-       * Each color plane shall be sent contiguously. For RGB images,
-       * this means the order of the pixel values sent is R1, R2, R3,
-       * ..., G1, G2, G3, ..., B1, B2, B3, etc.
-       **/
-      assert(frameOffset_ % samplesPerPixel_ == 0);
-      pixel += channel * frameOffset_ / samplesPerPixel_ + x * bytesPerPixel_;
+      pixel += channel * information_.GetBytesPerValue() + x * information_.GetChannelCount() * information_.GetBytesPerValue();
     }
 
     uint32_t v;
     v = pixel[0];
-    if (bytesPerPixel_ >= 2)
+    if (information_.GetBytesPerValue() >= 2)
       v = v + (static_cast<uint32_t>(pixel[1]) << 8);
-    if (bytesPerPixel_ >= 3)
+    if (information_.GetBytesPerValue() >= 3)
       v = v + (static_cast<uint32_t>(pixel[2]) << 16);
-    if (bytesPerPixel_ >= 4)
+    if (information_.GetBytesPerValue() >= 4)
       v = v + (static_cast<uint32_t>(pixel[3]) << 24);
 
-    v = v >> shift_;
+    v = v >> information_.GetShift();
 
     if (v & signMask_)
     {
@@ -253,7 +186,7 @@
 
   void DicomIntegerPixelAccessor::SetCurrentFrame(unsigned int frame)
   {
-    if (frame >= numberOfFrames_)
+    if (frame >= information_.GetNumberOfFrames())
     {
       throw OrthancException(ErrorCode_ParameterOutOfRange);
     }
--- a/Core/DicomFormat/DicomIntegerPixelAccessor.h	Mon Jun 02 13:24:44 2014 +0200
+++ b/Core/DicomFormat/DicomIntegerPixelAccessor.h	Wed Jun 25 11:56:48 2014 +0200
@@ -34,6 +34,8 @@
 
 #include "DicomMap.h"
 
+#include "DicomImageInformation.h"
+
 #include <stdint.h>
 
 namespace Orthanc
@@ -41,20 +43,14 @@
   class DicomIntegerPixelAccessor
   {
   private:
-    unsigned int width_;
-    unsigned int height_;
-    unsigned int samplesPerPixel_;
-    unsigned int numberOfFrames_;
-    unsigned int planarConfiguration_;
+    DicomImageInformation information_;
+
+    uint32_t signMask_;
+    uint32_t mask_;
+
     const void* pixelData_;
     size_t size_;
-
-    uint8_t shift_;
-    uint32_t signMask_;
-    uint32_t mask_;
-    size_t bytesPerPixel_;
     unsigned int frame_;
-
     size_t frameOffset_;
     size_t rowOffset_;
 
@@ -63,19 +59,9 @@
                               const void* pixelData,
                               size_t size);
 
-    unsigned int GetWidth() const
-    {
-      return width_;
-    }
-
-    unsigned int GetHeight() const
+    const DicomImageInformation GetInformation() const
     {
-      return height_;
-    }
-
-    unsigned int GetNumberOfFrames() const
-    {
-      return numberOfFrames_;
+      return information_;
     }
 
     unsigned int GetCurrentFrame() const
@@ -88,11 +74,11 @@
     void GetExtremeValues(int32_t& min, 
                           int32_t& max) const;
 
-    unsigned int GetChannelCount() const
+    int32_t GetValue(unsigned int x, unsigned int y, unsigned int channel = 0) const;
+
+    const void* GetPixelData() const
     {
-      return samplesPerPixel_;
+      return pixelData_;
     }
-
-    int32_t GetValue(unsigned int x, unsigned int y, unsigned int channel = 0) const;
   };
 }
--- a/Core/Enumerations.h	Mon Jun 02 13:24:44 2014 +0200
+++ b/Core/Enumerations.h	Wed Jun 25 11:56:48 2014 +0200
@@ -69,7 +69,9 @@
     ErrorCode_FullStorage,
     ErrorCode_CorruptedFile,
     ErrorCode_InexistentTag,
-    ErrorCode_ReadOnly
+    ErrorCode_ReadOnly,
+    ErrorCode_IncompatibleImageFormat,
+    ErrorCode_IncompatibleImageSize
   };
 
   /**
@@ -80,34 +82,34 @@
     /**
      * {summary}{Color image in RGB24 format.}
      * {description}{This format describes a color image. The pixels are stored in 3
-     * consecutive bytes. The memory layout is RGB.
+     * consecutive bytes. The memory layout is RGB.}
      **/
-    PixelFormat_RGB24,
+    PixelFormat_RGB24 = 1,
 
     /**
      * {summary}{Color image in RGBA32 format.}
      * {description}{This format describes a color image. The pixels are stored in 4
-     * consecutive bytes. The memory layout is RGBA.
+     * consecutive bytes. The memory layout is RGBA.}
      **/
-    PixelFormat_RGBA32,
+    PixelFormat_RGBA32 = 2,
 
     /**
      * {summary}{Graylevel 8bpp image.}
      * {description}{The image is graylevel. Each pixel is unsigned and stored in one byte.}
      **/
-    PixelFormat_Grayscale8,
+    PixelFormat_Grayscale8 = 3,
       
     /**
      * {summary}{Graylevel, unsigned 16bpp image.}
      * {description}{The image is graylevel. Each pixel is unsigned and stored in two bytes.}
      **/
-    PixelFormat_Grayscale16,
+    PixelFormat_Grayscale16 = 4,
       
     /**
      * {summary}{Graylevel, signed 16bpp image.}
      * {description}{The image is graylevel. Each pixel is signed and stored in two bytes.}
      **/
-    PixelFormat_SignedGrayscale16
+    PixelFormat_SignedGrayscale16 = 5
   };
 
 
@@ -120,22 +122,22 @@
      * {summary}{Rescaled to 8bpp.}
      * {description}{The minimum value of the image is set to 0, and its maximum value is set to 255.}
      **/
-    ImageExtractionMode_Preview,
+    ImageExtractionMode_Preview = 1,
 
     /**
      * {summary}{Truncation to the [0, 255] range.}
      **/
-    ImageExtractionMode_UInt8,
+    ImageExtractionMode_UInt8 = 2,
 
     /**
      * {summary}{Truncation to the [0, 65535] range.}
      **/
-    ImageExtractionMode_UInt16,
+    ImageExtractionMode_UInt16 = 3,
 
     /**
      * {summary}{Truncation to the [-32768, 32767] range.}
      **/
-    ImageExtractionMode_Int16
+    ImageExtractionMode_Int16 = 4
   };
 
 
--- a/Core/ImageFormats/ImageAccessor.cpp	Mon Jun 02 13:24:44 2014 +0200
+++ b/Core/ImageFormats/ImageAccessor.cpp	Wed Jun 25 11:56:48 2014 +0200
@@ -34,15 +34,77 @@
 #include "ImageAccessor.h"
 
 #include "../OrthancException.h"
+#include "../ChunkedBuffer.h"
 
 #include <stdint.h>
+#include <cassert>
+#include <glog/logging.h>
+#include <boost/lexical_cast.hpp>
 
 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_)
     {
+      LOG(ERROR) << "Trying to write on a read-only image";
       throw OrthancException(ErrorCode_ReadOnly);
     }
 
@@ -67,6 +129,7 @@
   {
     if (readOnly_)
     {
+      LOG(ERROR) << "Trying to write on a read-only image";
       throw OrthancException(ErrorCode_ReadOnly);
     }
 
@@ -104,6 +167,8 @@
     height_ = height;
     pitch_ = pitch;
     buffer_ = const_cast<void*>(buffer);
+
+    assert(GetBytesPerPixel(format_) * width_ <= pitch_);
   }
 
 
@@ -119,5 +184,38 @@
     height_ = height;
     pitch_ = pitch;
     buffer_ = buffer;
+
+    assert(GetBytesPerPixel(format_) * 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);
+  }
+
 }
--- a/Core/ImageFormats/ImageAccessor.h	Mon Jun 02 13:24:44 2014 +0200
+++ b/Core/ImageFormats/ImageAccessor.h	Wed Jun 25 11:56:48 2014 +0200
@@ -77,6 +77,11 @@
       return pitch_;
     }
 
+    unsigned int GetSize() const
+    {
+      return GetHeight() * GetPitch();
+    }
+
     const void* GetConstBuffer() const
     {
       return buffer_;
@@ -101,5 +106,7 @@
                         unsigned int height,
                         unsigned int pitch,
                         void *buffer);
+
+    void ToMatlabString(std::string& target) const; 
   };
 }
--- a/Core/ImageFormats/ImageBuffer.cpp	Mon Jun 02 13:24:44 2014 +0200
+++ b/Core/ImageFormats/ImageBuffer.cpp	Wed Jun 25 11:56:48 2014 +0200
@@ -33,22 +33,40 @@
 #include "../PrecompiledHeaders.h"
 #include "ImageBuffer.h"
 
+#include "../OrthancException.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
 namespace Orthanc
 {
   void ImageBuffer::Allocate()
   {
     if (changed_)
     {
-      pitch_ = GetBytesPerPixel() * width_;
+      Deallocate();
 
-      data_.resize(pitch_ * height_);
-      if (data_.size() > 0)
+      /*
+        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_ = &data_[0];
+        buffer_ = NULL;
       }
       else
       {
-        buffer_ = 0;
+        buffer_ = malloc(size);
+        if (buffer_ == NULL)
+        {
+          throw OrthancException(ErrorCode_NotEnoughMemory);
+        }
       }
 
       changed_ = false;
@@ -56,9 +74,32 @@
   }
 
 
-  ImageBuffer::ImageBuffer() : 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;
@@ -69,22 +110,31 @@
 
   void ImageBuffer::SetFormat(PixelFormat format)
   {
-    changed_ = true;
-    format_ = format;
+    if (format != format_)
+    {
+      changed_ = true;
+      format_ = format;
+    }
   }
 
 
   void ImageBuffer::SetWidth(unsigned int width)
   {
-    changed_ = true;
-    width_ = width;     
+    if (width != width_)
+    {
+      changed_ = true;
+      width_ = width;     
+    }
   }
 
 
   void ImageBuffer::SetHeight(unsigned int height)
   {
-    changed_ = true;
-    height_ = height;     
+    if (height != height_)
+    {
+      changed_ = true;
+      height_ = height;     
+    }
   }
 
 
@@ -106,4 +156,37 @@
     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();
+  }
 }
--- a/Core/ImageFormats/ImageBuffer.h	Mon Jun 02 13:24:44 2014 +0200
+++ b/Core/ImageFormats/ImageBuffer.h	Wed Jun 25 11:56:48 2014 +0200
@@ -36,25 +36,42 @@
 
 #include <vector>
 #include <stdint.h>
+#include <boost/noncopyable.hpp>
 
 namespace Orthanc
 {
-  class ImageBuffer
+  class ImageBuffer : public boost::noncopyable
   {
   private:
     bool changed_;
-    std::vector<uint8_t> data_;
 
+    bool forceMinimalPitch_;  // Currently unused
     PixelFormat format_;
     unsigned int width_;
     unsigned int height_;
     unsigned int pitch_;
-    uint8_t *buffer_;
+    void *buffer_;
+
+    void Initialize();
     
     void Allocate();
 
+    void Deallocate();
+
   public:
-    ImageBuffer();
+    ImageBuffer(unsigned int width,
+                unsigned int height,
+                PixelFormat format);
+
+    ImageBuffer()
+    {
+      Initialize();
+    }
+
+    ~ImageBuffer()
+    {
+      Deallocate();
+    }
 
     PixelFormat GetFormat() const
     {
@@ -85,5 +102,14 @@
     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/Core/ImageFormats/ImageProcessing.cpp	Wed Jun 25 11:56:48 2014 +0200
@@ -0,0 +1,473 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2014 Medical Physics Department, CHU 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 "ImageProcessing.h"
+
+#include "../OrthancException.h"
+
+#include <boost/math/special_functions/round.hpp>
+
+#include <cassert>
+#include <string.h>
+#include <limits>
+#include <stdint.h>
+
+namespace Orthanc
+{
+  template <typename TargetType, typename SourceType>
+  static void ConvertInternal(ImageAccessor& target,
+                              const ImageAccessor& source)
+  {
+    const TargetType minValue = std::numeric_limits<TargetType>::min();
+    const TargetType maxValue = std::numeric_limits<TargetType>::max();
+
+    for (unsigned int y = 0; y < source.GetHeight(); y++)
+    {
+      TargetType* t = reinterpret_cast<TargetType*>(target.GetRow(y));
+      const SourceType* s = reinterpret_cast<const SourceType*>(source.GetConstRow(y));
+
+      for (unsigned int x = 0; x < source.GetWidth(); x++, t++, s++)
+      {
+        if (static_cast<int32_t>(*s) < static_cast<int32_t>(minValue))
+        {
+          *t = minValue;
+        }
+        else if (static_cast<int32_t>(*s) > static_cast<int32_t>(maxValue))
+        {
+          *t = maxValue;
+        }
+        else
+        {
+          *t = static_cast<TargetType>(*s);
+        }
+      }
+    }
+  }
+
+
+  template <typename PixelType>
+  static void SetInternal(ImageAccessor& image,
+                          int64_t constant)
+  {
+    for (unsigned int y = 0; y < image.GetHeight(); y++)
+    {
+      PixelType* p = reinterpret_cast<PixelType*>(image.GetRow(y));
+
+      for (unsigned int x = 0; x < image.GetWidth(); x++, p++)
+      {
+        *p = static_cast<PixelType>(constant);
+      }
+    }
+  }
+
+
+  template <typename PixelType>
+  static void GetMinMaxValueInternal(PixelType& minValue,
+                                     PixelType& maxValue,
+                                     const ImageAccessor& source)
+  {
+    // Deal with the special case of empty image
+    if (source.GetWidth() == 0 ||
+        source.GetHeight() == 0)
+    {
+      minValue = 0;
+      maxValue = 0;
+      return;
+    }
+
+    minValue = std::numeric_limits<PixelType>::max();
+    maxValue = std::numeric_limits<PixelType>::min();
+
+    for (unsigned int y = 0; y < source.GetHeight(); y++)
+    {
+      const PixelType* p = reinterpret_cast<const PixelType*>(source.GetConstRow(y));
+
+      for (unsigned int x = 0; x < source.GetWidth(); x++, p++)
+      {
+        if (*p < minValue)
+        {
+          minValue = *p;
+        }
+
+        if (*p > maxValue)
+        {
+          maxValue = *p;
+        }
+      }
+    }
+  }
+
+
+
+  template <typename PixelType>
+  static void AddConstantInternal(ImageAccessor& image,
+                                  int64_t constant)
+  {
+    if (constant == 0)
+    {
+      return;
+    }
+
+    const int64_t minValue = std::numeric_limits<PixelType>::min();
+    const int64_t maxValue = std::numeric_limits<PixelType>::max();
+
+    for (unsigned int y = 0; y < image.GetHeight(); y++)
+    {
+      PixelType* p = reinterpret_cast<PixelType*>(image.GetRow(y));
+
+      for (unsigned int x = 0; x < image.GetWidth(); x++, p++)
+      {
+        int64_t v = static_cast<int64_t>(*p) + constant;
+
+        if (v > maxValue)
+        {
+          *p = std::numeric_limits<PixelType>::max();
+        }
+        else if (v < minValue)
+        {
+          *p = std::numeric_limits<PixelType>::min();
+        }
+        else
+        {
+          *p = static_cast<PixelType>(v);
+        }
+      }
+    }
+  }
+
+
+
+  template <typename PixelType>
+  void MultiplyConstantInternal(ImageAccessor& image,
+                                float factor)
+  {
+    if (abs(factor - 1.0f) <= std::numeric_limits<float>::epsilon())
+    {
+      return;
+    }
+
+    const int64_t minValue = std::numeric_limits<PixelType>::min();
+    const int64_t maxValue = std::numeric_limits<PixelType>::max();
+
+    for (unsigned int y = 0; y < image.GetHeight(); y++)
+    {
+      PixelType* p = reinterpret_cast<PixelType*>(image.GetRow(y));
+
+      for (unsigned int x = 0; x < image.GetWidth(); x++, p++)
+      {
+        int64_t v = boost::math::llround(static_cast<float>(*p) * factor);
+
+        if (v > maxValue)
+        {
+          *p = std::numeric_limits<PixelType>::max();
+        }
+        else if (v < minValue)
+        {
+          *p = std::numeric_limits<PixelType>::min();
+        }
+        else
+        {
+          *p = static_cast<PixelType>(v);
+        }
+      }
+    }
+  }
+
+
+  template <typename PixelType>
+  void ShiftScaleInternal(ImageAccessor& image,
+                          float offset,
+                          float scaling)
+  {
+    const float minValue = static_cast<float>(std::numeric_limits<PixelType>::min());
+    const float maxValue = static_cast<float>(std::numeric_limits<PixelType>::max());
+
+    for (unsigned int y = 0; y < image.GetHeight(); y++)
+    {
+      PixelType* p = reinterpret_cast<PixelType*>(image.GetRow(y));
+
+      for (unsigned int x = 0; x < image.GetWidth(); x++, p++)
+      {
+        float v = (static_cast<float>(*p) + offset) * scaling;
+
+        if (v > maxValue)
+        {
+          *p = std::numeric_limits<PixelType>::max();
+        }
+        else if (v < minValue)
+        {
+          *p = std::numeric_limits<PixelType>::min();
+        }
+        else
+        {
+          *p = static_cast<PixelType>(boost::math::iround(v));
+        }
+      }
+    }
+  }
+
+
+  void ImageProcessing::Copy(ImageAccessor& target,
+                             const ImageAccessor& source)
+  {
+    if (target.GetWidth() != source.GetWidth() ||
+        target.GetHeight() != source.GetHeight())
+    {
+      throw OrthancException(ErrorCode_IncompatibleImageSize);
+    }
+
+    if (target.GetFormat() != source.GetFormat())
+    {
+      throw OrthancException(ErrorCode_IncompatibleImageFormat);
+    }
+
+    unsigned int lineSize = GetBytesPerPixel(source.GetFormat()) * source.GetWidth();
+
+    assert(source.GetPitch() >= lineSize && target.GetPitch() >= lineSize);
+
+    for (unsigned int y = 0; y < source.GetHeight(); y++)
+    {
+      memcpy(target.GetRow(y), source.GetConstRow(y), lineSize);
+    }
+  }
+
+
+  void ImageProcessing::Convert(ImageAccessor& target,
+                                const ImageAccessor& source)
+  {
+    if (target.GetWidth() != source.GetWidth() ||
+        target.GetHeight() != source.GetHeight())
+    {
+      throw OrthancException(ErrorCode_IncompatibleImageSize);
+    }
+
+    if (source.GetFormat() == target.GetFormat())
+    {
+      Copy(target, source);
+      return;
+    }
+
+    if (target.GetFormat() == PixelFormat_Grayscale16 &&
+        source.GetFormat() == PixelFormat_Grayscale8)
+    {
+      ConvertInternal<uint16_t, uint8_t>(target, source);
+      return;
+    }
+
+    if (target.GetFormat() == PixelFormat_SignedGrayscale16 &&
+        source.GetFormat() == PixelFormat_Grayscale8)
+    {
+      ConvertInternal<int16_t, uint8_t>(target, source);
+      return;
+    }
+
+    if (target.GetFormat() == PixelFormat_Grayscale8 &&
+        source.GetFormat() == PixelFormat_Grayscale16)
+    {
+      ConvertInternal<uint8_t, uint16_t>(target, source);
+      return;
+    }
+
+    if (target.GetFormat() == PixelFormat_SignedGrayscale16 &&
+        source.GetFormat() == PixelFormat_Grayscale16)
+    {
+      ConvertInternal<int16_t, uint16_t>(target, source);
+      return;
+    }
+
+    if (target.GetFormat() == PixelFormat_Grayscale8 &&
+        source.GetFormat() == PixelFormat_SignedGrayscale16)
+    {
+      ConvertInternal<uint8_t, int16_t>(target, source);
+      return;
+    }
+
+    if (target.GetFormat() == PixelFormat_Grayscale16 &&
+        source.GetFormat() == PixelFormat_SignedGrayscale16)
+    {
+      ConvertInternal<uint16_t, int16_t>(target, source);
+      return;
+    }
+
+    throw OrthancException(ErrorCode_NotImplemented);
+  }
+
+
+
+  void ImageProcessing::Set(ImageAccessor& image,
+                            int64_t value)
+  {
+    switch (image.GetFormat())
+    {
+      case PixelFormat_Grayscale8:
+        SetInternal<uint8_t>(image, value);
+        return;
+
+      case PixelFormat_Grayscale16:
+        SetInternal<uint16_t>(image, value);
+        return;
+
+      case PixelFormat_SignedGrayscale16:
+        SetInternal<int16_t>(image, value);
+        return;
+
+      default:
+        throw OrthancException(ErrorCode_NotImplemented);
+    }
+  }
+
+
+  void ImageProcessing::ShiftRight(ImageAccessor& image,
+                                   unsigned int shift)
+  {
+    if (image.GetWidth() == 0 ||
+        image.GetHeight() == 0 ||
+        shift == 0)
+    {
+      // Nothing to do
+      return;
+    }
+
+    throw OrthancException(ErrorCode_NotImplemented);
+  }
+
+
+  void ImageProcessing::GetMinMaxValue(int64_t& minValue,
+                                       int64_t& maxValue,
+                                       const ImageAccessor& image)
+  {
+    switch (image.GetFormat())
+    {
+      case PixelFormat_Grayscale8:
+      {
+        uint8_t a, b;
+        GetMinMaxValueInternal<uint8_t>(a, b, image);
+        minValue = a;
+        maxValue = b;
+        break;
+      }
+
+      case PixelFormat_Grayscale16:
+      {
+        uint16_t a, b;
+        GetMinMaxValueInternal<uint16_t>(a, b, image);
+        minValue = a;
+        maxValue = b;
+        break;
+      }
+
+      case PixelFormat_SignedGrayscale16:
+      {
+        int16_t a, b;
+        GetMinMaxValueInternal<int16_t>(a, b, image);
+        minValue = a;
+        maxValue = b;
+        break;
+      }
+
+      default:
+        throw OrthancException(ErrorCode_NotImplemented);
+    }
+  }
+
+
+
+  void ImageProcessing::AddConstant(ImageAccessor& image,
+                                    int64_t value)
+  {
+    switch (image.GetFormat())
+    {
+      case PixelFormat_Grayscale8:
+        AddConstantInternal<uint8_t>(image, value);
+        return;
+
+      case PixelFormat_Grayscale16:
+        AddConstantInternal<uint16_t>(image, value);
+        return;
+
+      case PixelFormat_SignedGrayscale16:
+        AddConstantInternal<int16_t>(image, value);
+        return;
+
+      default:
+        throw OrthancException(ErrorCode_NotImplemented);
+    }
+  }
+
+
+  void ImageProcessing::MultiplyConstant(ImageAccessor& image,
+                                         float factor)
+  {
+    switch (image.GetFormat())
+    {
+      case PixelFormat_Grayscale8:
+        MultiplyConstantInternal<uint8_t>(image, factor);
+        return;
+
+      case PixelFormat_Grayscale16:
+        MultiplyConstantInternal<uint16_t>(image, factor);
+        return;
+
+      case PixelFormat_SignedGrayscale16:
+        MultiplyConstantInternal<int16_t>(image, factor);
+        return;
+
+      default:
+        throw OrthancException(ErrorCode_NotImplemented);
+    }
+  }
+
+
+  void ImageProcessing::ShiftScale(ImageAccessor& image,
+                                   float offset,
+                                   float scaling)
+  {
+    switch (image.GetFormat())
+    {
+      case PixelFormat_Grayscale8:
+        ShiftScaleInternal<uint8_t>(image, offset, scaling);
+        return;
+
+      case PixelFormat_Grayscale16:
+        ShiftScaleInternal<uint16_t>(image, offset, scaling);
+        return;
+
+      case PixelFormat_SignedGrayscale16:
+        ShiftScaleInternal<int16_t>(image, offset, scaling);
+        return;
+
+      default:
+        throw OrthancException(ErrorCode_NotImplemented);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/ImageFormats/ImageProcessing.h	Wed Jun 25 11:56:48 2014 +0200
@@ -0,0 +1,70 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2014 Medical Physics Department, CHU 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 <stdint.h>
+
+namespace Orthanc
+{
+  class ImageProcessing
+  {
+  public:
+    static void Copy(ImageAccessor& target,
+                     const ImageAccessor& source);
+
+    static void Convert(ImageAccessor& target,
+                        const ImageAccessor& source);
+
+    static void Set(ImageAccessor& image,
+                    int64_t value);
+
+    static void ShiftRight(ImageAccessor& target,
+                           unsigned int shift);
+
+    static void GetMinMaxValue(int64_t& minValue,
+                               int64_t& maxValue,
+                               const ImageAccessor& image);
+
+    static void AddConstant(ImageAccessor& image,
+                            int64_t value);
+
+    static void MultiplyConstant(ImageAccessor& image,
+                                 float factor);
+
+    static void ShiftScale(ImageAccessor& image,
+                           float offset,
+                           float scaling);
+  };
+}
--- a/Core/ImageFormats/PngWriter.h	Mon Jun 02 13:24:44 2014 +0200
+++ b/Core/ImageFormats/PngWriter.h	Wed Jun 25 11:56:48 2014 +0200
@@ -32,7 +32,7 @@
 
 #pragma once
 
-#include "../Enumerations.h"
+#include "ImageAccessor.h"
 
 #include <boost/shared_ptr.hpp>
 #include <string>
@@ -74,5 +74,19 @@
                        unsigned int pitch,
                        PixelFormat format,
                        const void* buffer);
+
+    void WriteToFile(const char* filename,
+                     const ImageAccessor& accessor)
+    {
+      WriteToFile(filename, accessor.GetWidth(), accessor.GetHeight(),
+                  accessor.GetPitch(), accessor.GetFormat(), accessor.GetConstBuffer());
+    }
+
+    void WriteToMemory(std::string& png,
+                       const ImageAccessor& accessor)
+    {
+      WriteToMemory(png, accessor.GetWidth(), accessor.GetHeight(),
+                    accessor.GetPitch(), accessor.GetFormat(), accessor.GetConstBuffer());
+    }
   };
 }
--- a/Core/MultiThreading/Mutex.cpp	Mon Jun 02 13:24:44 2014 +0200
+++ b/Core/MultiThreading/Mutex.cpp	Wed Jun 25 11:56:48 2014 +0200
@@ -37,7 +37,7 @@
 
 #if defined(_WIN32)
 #include <windows.h>
-#elif defined(__linux)
+#elif defined(__linux) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
 #include <pthread.h>
 #else
 #error Support your platform here
@@ -75,7 +75,7 @@
   }
 
 
-#elif defined(__linux)
+#elif defined(__linux) || defined(__FreeBSD_kernel__) || defined(__APPLE__)
 
   struct Mutex::PImpl
   {
--- a/Core/MultiThreading/ReaderWriterLock.h	Mon Jun 02 13:24:44 2014 +0200
+++ b/Core/MultiThreading/ReaderWriterLock.h	Wed Jun 25 11:56:48 2014 +0200
@@ -34,9 +34,11 @@
 
 #include "ILockable.h"
 
+#include <boost/noncopyable.hpp>
+
 namespace Orthanc
 {
-  class ReaderWriterLock
+  class ReaderWriterLock : public boost::noncopyable
   {
   private:
     struct PImpl;
--- a/Core/OrthancException.cpp	Mon Jun 02 13:24:44 2014 +0200
+++ b/Core/OrthancException.cpp	Wed Jun 25 11:56:48 2014 +0200
@@ -115,6 +115,12 @@
       case ErrorCode_ReadOnly:
         return "Cannot modify a read-only data structure";
 
+      case ErrorCode_IncompatibleImageSize:
+        return "Incompatible size of the images";
+
+      case ErrorCode_IncompatibleImageFormat:
+        return "Incompatible format of the images";
+
       case ErrorCode_Custom:
       default:
         return "???";
--- a/Core/Toolbox.cpp	Mon Jun 02 13:24:44 2014 +0200
+++ b/Core/Toolbox.cpp	Wed Jun 25 11:56:48 2014 +0200
@@ -54,17 +54,17 @@
 #include <limits.h>      /* PATH_MAX */
 #endif
 
-#if defined(__linux)
+#if defined(__linux) || defined(__FreeBSD_kernel__)
 #include <limits.h>      /* PATH_MAX */
 #include <signal.h>
 #include <unistd.h>
 #endif
 
-#if BOOST_HAS_LOCALE == 1
+#if BOOST_HAS_LOCALE != 1
+#error Since version 0.7.6, Orthanc entirely relies on boost::locale
+#endif
+
 #include <boost/locale.hpp>
-#else
-#include <iconv.h>
-#endif
 
 #include "../Resources/md5/md5.h"
 #include "../Resources/base64/base64.h"
@@ -74,68 +74,11 @@
 // Patch for the missing "_strtoll" symbol when compiling with Visual Studio
 extern "C"
 {
-int64_t _strtoi64(const char *nptr, char **endptr, int base);
-int64_t strtoll(const char *nptr, char **endptr, int base)
-{
-    return _strtoi64(nptr, endptr, base);
-} 
-}
-#endif
-
-
-#if BOOST_HAS_LOCALE == 0
-namespace
-{
-  class IconvRabi
+  int64_t _strtoi64(const char *nptr, char **endptr, int base);
+  int64_t strtoll(const char *nptr, char **endptr, int base)
   {
-  private:
-    iconv_t context_;
-
-  public:
-    IconvRabi(const char* tocode, const char* fromcode)
-    {
-      context_ = iconv_open(tocode, fromcode);
-      if (!context_)
-      {
-        throw Orthanc::OrthancException("Unknown code page");
-      }
-    }
-    
-    ~IconvRabi()
-    {
-      iconv_close(context_);
-    }
-
-    std::string Convert(const std::string& source)
-    {
-      if (source.size() == 0)
-      {
-        return "";
-      }
-
-      std::string result;
-      char* sourcePos = const_cast<char*>(&source[0]);
-      size_t sourceLeft = source.size();
-
-      std::vector<char> storage(source.size() + 10);
-      
-      while (sourceLeft > 0)
-      {
-        char* tmp = &storage[0];
-        size_t outputLeft = storage.size();
-        size_t err = iconv(context_, &sourcePos, &sourceLeft, &tmp, &outputLeft);
-        if (err < 0)
-        {
-          throw Orthanc::OrthancException("Bad character in sequence");
-        }
-
-        size_t count = storage.size() - outputLeft;
-        result += std::string(&storage[0], count);
-      }
-
-      return result;
-    }
-  };
+    return _strtoi64(nptr, endptr, base);
+  } 
 }
 #endif
 
@@ -162,7 +105,7 @@
   {
 #if defined(_WIN32)
     ::Sleep(static_cast<DWORD>(microSeconds / static_cast<uint64_t>(1000)));
-#elif defined(__linux)
+#elif defined(__linux) || defined(__APPLE__) || defined(__FreeBSD_kernel__)
     usleep(microSeconds);
 #else
 #error Support your platform here
@@ -515,7 +458,7 @@
     return std::string(&buffer[0]);
   }
 
-#elif defined(__linux)
+#elif defined(__linux) || defined(__FreeBSD_kernel__)
   std::string Toolbox::GetPathToExecutable()
   {
     std::vector<char> buffer(PATH_MAX + 1);
@@ -554,7 +497,6 @@
   std::string Toolbox::ConvertToUtf8(const std::string& source,
                                      const char* fromEncoding)
   {
-#if BOOST_HAS_LOCALE == 1
     try
     {
       return boost::locale::conv::to_utf<char>(source, fromEncoding);
@@ -564,17 +506,6 @@
       // Bad input string or bad encoding
       return ConvertToAscii(source);
     }
-#else
-    IconvRabi iconv("UTF-8", fromEncoding);
-    try
-    {
-      return iconv.Convert(source);
-    }
-    catch (OrthancException)
-    {
-      return ConvertToAscii(source);
-    }
-#endif
   }
 
 
@@ -582,7 +513,7 @@
   {
     std::string result;
 
-    result.reserve(source.size());
+    result.reserve(source.size() + 1);
     for (size_t i = 0; i < source.size(); i++)
     {
       if (source[i] < 128 && source[i] >= 0 && !iscntrl(source[i]))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DarwinCompilation.txt	Wed Jun 25 11:56:48 2014 +0200
@@ -0,0 +1,52 @@
+This file is a complement to "INSTALL", which contains instructions
+that are specific to Mac OS X (Darwin).
+
+
+Static linking for OS X using XCode
+===================================
+
+The most simple way of building Orthanc under OS X consists in
+statically linking against all the third-party dependencies. In this
+case, no package manager such as Homebrew or MacPorts is required.
+The build tool (CMake) will download the sources of all the required
+packages and automatically compile them.
+
+
+Prerequisites
+-------------
+
+1) XCode must be installed.
+
+2) CMake must be installed (http://www.cmake.org/).
+
+3) It is assumed that Orthanc source code is placed in the folder
+   "~/Orthanc" and that the binaries will be compiled to
+   "~/OrthancBuild".
+
+
+Prepare the build with CMake
+----------------------------
+
+# cd ~/OrthancBuild
+# cmake -GXcode -DCMAKE_OSX_DEPLOYMENT_TARGET=10.8 -DSTATIC_BUILD=ON -DSTANDALONE_BUILD=ON ..
+
+NB: Adapt the value of "CMAKE_OSX_DEPLOYMENT_TARGET" with respect to
+your version of XCode.
+
+
+Build the Debug version of Orthanc
+----------------------------------
+
+# xcodebuild
+# ./Debug/UnitTests
+
+The binaries of Orthanc are located at "~/OrthancBuild/Debug/Orthanc".
+
+
+Build the Release version of Orthanc
+------------------------------------
+
+# xcodebuild -configuration Release
+# ./Debug/UnitTests
+
+The binaries of Orthanc are located at "~/OrthancBuild/Release/Orthanc".
--- a/INSTALL	Mon Jun 02 13:24:44 2014 +0200
+++ b/INSTALL	Wed Jun 25 11:56:48 2014 +0200
@@ -47,6 +47,13 @@
 
 
 
+Native OS X Compilation
+-----------------------
+
+See the file "DarwinCompilation.txt".
+
+
+
 Native Windows build with Microsoft Visual Studio 2005
 ------------------------------------------------------
 
@@ -71,7 +78,7 @@
 the following command:
 
 # cd ~/OrthancBuild
-# cmake -DCMAKE_TOOLCHAIN_FILE=~/Orthanc/Resources/MinGWToolchain.cmake -DSTANDALONE_BUILD=ON -DCMAKE_BUILD_TYPE=Debug ~/Orthanc
+# cmake -DCMAKE_TOOLCHAIN_FILE=~/Orthanc/Resources/MinGWToolchain.cmake -DSTATIC_BUILD=ON -DSTANDALONE_BUILD=ON -DCMAKE_BUILD_TYPE=Debug ~/Orthanc
 # make
 
 
--- a/LinuxCompilation.txt	Mon Jun 02 13:24:44 2014 +0200
+++ b/LinuxCompilation.txt	Wed Jun 25 11:56:48 2014 +0200
@@ -12,6 +12,10 @@
 automatically compile them. This process should work on all the Linux
 distributions.
 
+We make the assumption that Orthanc source code is placed in the
+folder "~/Orthanc" and that the binaries will be compiled to
+"~/OrthancBuild".
+
 
 To build binaries with debug information:
 
@@ -73,6 +77,8 @@
 	-DUSE_SYSTEM_DCMTK=OFF \
 	-DUSE_SYSTEM_MONGOOSE=OFF \
 	-DUSE_SYSTEM_JSONCPP=OFF \
+        -DENABLE_JPEG=OFF \
+        -DENABLE_JPEG_LOSSLESS=OFF \
 	~/Orthanc 
 
 
@@ -89,6 +95,8 @@
         -DUSE_SYSTEM_GOOGLE_LOG=OFF \
 	-DUSE_SYSTEM_MONGOOSE=OFF \
         -DUSE_GTEST_DEBIAN_SOURCE_PACKAGE=ON \
+        -DENABLE_JPEG=OFF \
+        -DENABLE_JPEG_LOSSLESS=OFF \
 	~/Orthanc
 
 
@@ -104,6 +112,8 @@
 # cmake -DALLOW_DOWNLOADS=ON \
 	-DUSE_SYSTEM_MONGOOSE=OFF \
         -DUSE_GTEST_DEBIAN_SOURCE_PACKAGE=ON \
+        -DENABLE_JPEG=OFF \
+        -DENABLE_JPEG_LOSSLESS=OFF \
 	~/Orthanc
 
 Note: Have also a look at the official package:
@@ -124,6 +134,8 @@
 	-DUSE_SYSTEM_JSONCPP=OFF \
 	-DUSE_SYSTEM_GOOGLE_LOG=OFF \
         -DUSE_GTEST_DEBIAN_SOURCE_PACKAGE=ON \
+        -DENABLE_JPEG=OFF \
+        -DENABLE_JPEG_LOSSLESS=OFF \
 	~/Orthanc
 
 
@@ -134,13 +146,27 @@
        	       	       uuid-dev libcurl4-openssl-dev liblua5.1-0-dev \
        	       	       libgoogle-glog-dev libgtest-dev libpng-dev \
        	       	       libsqlite3-dev libssl-dev zlib1g-dev \
-       	       	       libdcmtk2-dev libboost-all-dev libwrap0-dev
+       	       	       libdcmtk2-dev libboost-all-dev libwrap0-dev libcharls-dev
+
+With JPEG:
+
+# cmake "-DDCMTK_LIBRARIES=CharLS;dcmjpls;wrap;oflog" \
+        -DALLOW_DOWNLOADS=ON \
+	-DUSE_SYSTEM_MONGOOSE=OFF \
+	-DUSE_SYSTEM_JSONCPP=OFF \
+        -DUSE_GTEST_DEBIAN_SOURCE_PACKAGE=ON \
+	~/Orthanc
+
+
+Without JPEG:
 
 # cmake "-DDCMTK_LIBRARIES=wrap;oflog" \
         -DALLOW_DOWNLOADS=ON \
 	-DUSE_SYSTEM_MONGOOSE=OFF \
 	-DUSE_SYSTEM_JSONCPP=OFF \
         -DUSE_GTEST_DEBIAN_SOURCE_PACKAGE=ON \
+        -DENABLE_JPEG=OFF \
+        -DENABLE_JPEG_LOSSLESS=OFF \
 	~/Orthanc
 
 
@@ -157,6 +183,8 @@
         -DALLOW_DOWNLOADS=ON \
 	-DUSE_SYSTEM_MONGOOSE=OFF \
         -DUSE_GTEST_DEBIAN_SOURCE_PACKAGE=ON \
+        -DENABLE_JPEG=OFF \
+        -DENABLE_JPEG_LOSSLESS=OFF \
 	~/Orthanc
 
 
@@ -169,8 +197,10 @@
                    gtest-devel libpng-devel libsqlite3x-devel libuuid-devel \
                    mongoose-devel openssl-devel jsoncpp-devel lua-devel
 
-# cmake ~/Orthanc
-
+# cmake -DENABLE_JPEG=OFF \
+        -DENABLE_JPEG_LOSSLESS=OFF \
+        ~/Orthanc
+       
 Note: Have also a look at the official package:
 http://pkgs.fedoraproject.org/cgit/orthanc.git/tree/?h=f18
 
--- a/NEWS	Mon Jun 02 13:24:44 2014 +0200
+++ b/NEWS	Wed Jun 25 11:56:48 2014 +0200
@@ -1,6 +1,16 @@
 Pending changes in the mainline
 ===============================
 
+* Official support of OS X (Darwin)
+* Options to limit the number of results for an incoming C-FIND query
+* Support of kFreeBSD
+
+
+Version 0.7.6 (2014/06/11)
+==========================
+
+* Support of JPEG and JPEG-LS decompression
+* Download DICOM images as Matlab/Octave arrays
 * Precompiled headers for Microsoft Visual Studio
 
 
--- a/OrthancCppClient/OrthancCppClient.cpp	Mon Jun 02 13:24:44 2014 +0200
+++ b/OrthancCppClient/OrthancCppClient.cpp	Wed Jun 25 11:56:48 2014 +0200
@@ -35,6 +35,7 @@
  * avoid problems with precompiled headers.
  **/
 
+#include "../Core/ChunkedBuffer.cpp"
 #include "../Core/Enumerations.cpp"
 #include "../Core/HttpClient.cpp"
 #include "../Core/ImageFormats/ImageAccessor.cpp"
--- a/OrthancCppClient/SharedLibrary/AUTOGENERATED/ExternC.cpp	Mon Jun 02 13:24:44 2014 +0200
+++ b/OrthancCppClient/SharedLibrary/AUTOGENERATED/ExternC.cpp	Wed Jun 25 11:56:48 2014 +0200
@@ -1486,12 +1486,12 @@
 
   LAAW_EXPORT_DLL_API const char* LAAW_CALL_CONVENTION LAAW_EXTERNC_GetFileVersion()
   {
-    return "0.7.0.5";
+    return "0.7.0.6";
   }
 
   LAAW_EXPORT_DLL_API const char* LAAW_CALL_CONVENTION LAAW_EXTERNC_GetFullVersion()
   {
-    return "0.7.5";
+    return "0.7.6";
   }
 
   LAAW_EXPORT_DLL_API void LAAW_CALL_CONVENTION LAAW_EXTERNC_FreeString(char* str)
--- a/OrthancCppClient/SharedLibrary/AUTOGENERATED/OrthancCppClient.h	Mon Jun 02 13:24:44 2014 +0200
+++ b/OrthancCppClient/SharedLibrary/AUTOGENERATED/OrthancCppClient.h	Wed Jun 25 11:56:48 2014 +0200
@@ -507,35 +507,35 @@
     * The image is graylevel. Each pixel is signed and stored in two bytes.
     *
     **/
-    PixelFormat_SignedGrayscale16 = 4,
+    PixelFormat_SignedGrayscale16 = 5,
     /**
     * @brief Color image in RGB24 format.
     *
-    * Color image in RGB24 format.
+    * This format describes a color image. The pixels are stored in 3 consecutive bytes. The memory layout is RGB.
     *
     **/
-    PixelFormat_RGB24 = 0,
+    PixelFormat_RGB24 = 1,
     /**
     * @brief Color image in RGBA32 format.
     *
-    * Color image in RGBA32 format.
+    * This format describes a color image. The pixels are stored in 4 consecutive bytes. The memory layout is RGBA.
     *
     **/
-    PixelFormat_RGBA32 = 1,
+    PixelFormat_RGBA32 = 2,
     /**
     * @brief Graylevel 8bpp image.
     *
     * The image is graylevel. Each pixel is unsigned and stored in one byte.
     *
     **/
-    PixelFormat_Grayscale8 = 2,
+    PixelFormat_Grayscale8 = 3,
     /**
     * @brief Graylevel, unsigned 16bpp image.
     *
     * The image is graylevel. Each pixel is unsigned and stored in two bytes.
     *
     **/
-    PixelFormat_Grayscale16 = 3
+    PixelFormat_Grayscale16 = 4
   };
 }
 
@@ -556,28 +556,28 @@
     * Truncation to the [-32768, 32767] range.
     *
     **/
-    ImageExtractionMode_Int16 = 3,
+    ImageExtractionMode_Int16 = 4,
     /**
     * @brief Rescaled to 8bpp.
     *
     * The minimum value of the image is set to 0, and its maximum value is set to 255.
     *
     **/
-    ImageExtractionMode_Preview = 0,
+    ImageExtractionMode_Preview = 1,
     /**
     * @brief Truncation to the [0, 255] range.
     *
     * Truncation to the [0, 255] range.
     *
     **/
-    ImageExtractionMode_UInt8 = 1,
+    ImageExtractionMode_UInt8 = 2,
     /**
     * @brief Truncation to the [0, 65535] range.
     *
     * Truncation to the [0, 65535] range.
     *
     **/
-    ImageExtractionMode_UInt16 = 2
+    ImageExtractionMode_UInt16 = 3
   };
 }
 
--- a/OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows32.rc	Mon Jun 02 13:24:44 2014 +0200
+++ b/OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows32.rc	Wed Jun 25 11:56:48 2014 +0200
@@ -1,7 +1,7 @@
 #include <winver.h>
 
 VS_VERSION_INFO VERSIONINFO
-   FILEVERSION 0,7,0,5
+   FILEVERSION 0,7,0,6
    PRODUCTVERSION 0,7,0,0
    FILEOS VOS_NT_WINDOWS32
    FILETYPE VFT_DLL
@@ -10,10 +10,10 @@
       BEGIN
          BLOCK "040904E4"
          BEGIN
-            VALUE "Comments", "Release 0.7.5"
+            VALUE "Comments", "Release 0.7.6"
             VALUE "CompanyName", "CHU of Liege"
             VALUE "FileDescription", "Native client to the REST API of Orthanc"
-            VALUE "FileVersion", "0.7.0.5"
+            VALUE "FileVersion", "0.7.0.6"
             VALUE "InternalName", "OrthancClient"
             VALUE "LegalCopyright", "(c) 2012-2014, Sebastien Jodogne, CHU of Liege"
             VALUE "LegalTrademarks", "Licensing information is available on https://code.google.com/p/orthanc/"
--- a/OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows64.rc	Mon Jun 02 13:24:44 2014 +0200
+++ b/OrthancCppClient/SharedLibrary/AUTOGENERATED/Windows64.rc	Wed Jun 25 11:56:48 2014 +0200
@@ -1,7 +1,7 @@
 #include <winver.h>
 
 VS_VERSION_INFO VERSIONINFO
-   FILEVERSION 0,7,0,5
+   FILEVERSION 0,7,0,6
    PRODUCTVERSION 0,7,0,0
    FILEOS VOS_NT_WINDOWS32
    FILETYPE VFT_DLL
@@ -10,10 +10,10 @@
       BEGIN
          BLOCK "040904E4"
          BEGIN
-            VALUE "Comments", "Release 0.7.5"
+            VALUE "Comments", "Release 0.7.6"
             VALUE "CompanyName", "CHU of Liege"
             VALUE "FileDescription", "Native client to the REST API of Orthanc"
-            VALUE "FileVersion", "0.7.0.5"
+            VALUE "FileVersion", "0.7.0.6"
             VALUE "InternalName", "OrthancClient"
             VALUE "LegalCopyright", "(c) 2012-2014, Sebastien Jodogne, CHU of Liege"
             VALUE "LegalTrademarks", "Licensing information is available on https://code.google.com/p/orthanc/"
--- a/OrthancCppClient/SharedLibrary/Product.json	Mon Jun 02 13:24:44 2014 +0200
+++ b/OrthancCppClient/SharedLibrary/Product.json	Wed Jun 25 11:56:48 2014 +0200
@@ -4,5 +4,5 @@
   "Company" : "CHU of Liege",
   "Copyright" : "(c) 2012-2014, Sebastien Jodogne, CHU of Liege",
   "Legal" : "Licensing information is available on https://code.google.com/p/orthanc/",
-  "Version" : "0.7.5"
+  "Version" : "0.7.6"
 }
--- a/OrthancServer/DicomModification.cpp	Mon Jun 02 13:24:44 2014 +0200
+++ b/OrthancServer/DicomModification.cpp	Wed Jun 25 11:56:48 2014 +0200
@@ -37,6 +37,7 @@
 #include "FromDcmtkBridge.h"
 
 #include <memory>   // For std::auto_ptr
+#include <glog/logging.h>
 
 namespace Orthanc
 {
@@ -238,6 +239,7 @@
 
     if (level_ == ResourceType_Patient && !IsReplaced(DICOM_TAG_PATIENT_ID))
     {
+      LOG(ERROR) << "When modifying a patient, her PatientID is required to be modified";
       throw OrthancException(ErrorCode_BadRequest);
     }
 
--- a/OrthancServer/DicomProtocol/DicomServer.cpp	Mon Jun 02 13:24:44 2014 +0200
+++ b/OrthancServer/DicomProtocol/DicomServer.cpp	Wed Jun 25 11:56:48 2014 +0200
@@ -116,7 +116,7 @@
     LoadEmbeddedDictionary(d, EmbeddedResources::DICTIONARY_DICOM);
     LoadEmbeddedDictionary(d, EmbeddedResources::DICTIONARY_PRIVATE);
 
-#elif defined(__linux)
+#elif defined(__linux) || defined(__FreeBSD_kernel__)
     std::string path = DCMTK_DICTIONARY_DIR;
 
     const char* env = std::getenv(DCM_DICT_ENVIRONMENT_VARIABLE);
--- a/OrthancServer/DicomProtocol/DicomUserConnection.cpp	Mon Jun 02 13:24:44 2014 +0200
+++ b/OrthancServer/DicomProtocol/DicomUserConnection.cpp	Wed Jun 25 11:56:48 2014 +0200
@@ -107,6 +107,18 @@
 #endif 
 
 
+#if !defined(HOST_NAME_MAX) && defined(_POSIX_HOST_NAME_MAX)
+/**
+ * TO IMPROVE: "_POSIX_HOST_NAME_MAX is only the minimum value that
+ * HOST_NAME_MAX can ever have [...] Therefore you cannot allocate an
+ * array of size _POSIX_HOST_NAME_MAX, invoke gethostname() and expect
+ * that the result will fit."
+ * http://lists.gnu.org/archive/html/bug-gnulib/2009-08/msg00128.html
+ **/
+#define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
+#endif
+
+
 static const char* DEFAULT_PREFERRED_TRANSFER_SYNTAX = UID_LittleEndianImplicitTransferSyntax;
 
 /**
@@ -205,21 +217,21 @@
     unsigned int presentationContextId = 1;
 
     for (std::list<std::string>::const_iterator it = reservedStorageSOPClasses_.begin();
-         it != reservedStorageSOPClasses_.end(); it++)
+         it != reservedStorageSOPClasses_.end(); ++it)
     {
       RegisterStorageSOPClass(pimpl_->params_, presentationContextId, 
                               *it, asPreferred, asFallback);
     }
 
     for (std::set<std::string>::const_iterator it = storageSOPClasses_.begin();
-         it != storageSOPClasses_.end(); it++)
+         it != storageSOPClasses_.end(); ++it)
     {
       RegisterStorageSOPClass(pimpl_->params_, presentationContextId, 
                               *it, asPreferred, asFallback);
     }
 
     for (std::set<std::string>::const_iterator it = defaultStorageSOPClasses_.begin();
-         it != defaultStorageSOPClasses_.end(); it++)
+         it != defaultStorageSOPClasses_.end(); ++it)
     {
       RegisterStorageSOPClass(pimpl_->params_, presentationContextId, 
                               *it, asPreferred, asFallback);
--- a/OrthancServer/DicomProtocol/IFindRequestHandler.h	Mon Jun 02 13:24:44 2014 +0200
+++ b/OrthancServer/DicomProtocol/IFindRequestHandler.h	Wed Jun 25 11:56:48 2014 +0200
@@ -47,7 +47,13 @@
     {
     }
 
-    virtual void Handle(DicomFindAnswers& answers,
+    /**
+     * Can throw exceptions. Returns "false" iff too many results have
+     * to be returned. In such a case, a "Matching terminated due to
+     * Cancel request" DIMSE code would be returned.
+     * https://www.dabsoft.ch/dicom/4/V.4.1/
+     **/
+    virtual bool Handle(DicomFindAnswers& answers,
                         const DicomMap& input,
                         const std::string& callingAETitle) = 0;
   };
--- a/OrthancServer/DicomProtocol/RemoteModalityParameters.cpp	Mon Jun 02 13:24:44 2014 +0200
+++ b/OrthancServer/DicomProtocol/RemoteModalityParameters.cpp	Wed Jun 25 11:56:48 2014 +0200
@@ -40,12 +40,12 @@
 
 namespace Orthanc
 {
-  RemoteModalityParameters::RemoteModalityParameters()
+  RemoteModalityParameters::RemoteModalityParameters() :
+    aet_("ORTHANC"),
+    host_("localhost"),
+    port_(104),
+    manufacturer_(ModalityManufacturer_Generic)
   {
-    aet_ = "ORTHANC";
-    host_ = "localhost";
-    port_ = 104;
-    manufacturer_ = ModalityManufacturer_Generic;
   }
 
   void RemoteModalityParameters::SetPort(int port)
--- a/OrthancServer/DicomProtocol/ReusableDicomUserConnection.h	Mon Jun 02 13:24:44 2014 +0200
+++ b/OrthancServer/DicomProtocol/ReusableDicomUserConnection.h	Wed Jun 25 11:56:48 2014 +0200
@@ -90,7 +90,7 @@
 
     unsigned int GetMillisecondsBeforeClose() const
     {
-      return timeBeforeClose_.total_milliseconds();
+      return static_cast<unsigned int>(timeBeforeClose_.total_milliseconds());
     }
 
     void SetMillisecondsBeforeClose(unsigned int ms);
--- a/OrthancServer/FromDcmtkBridge.cpp	Mon Jun 02 13:24:44 2014 +0200
+++ b/OrthancServer/FromDcmtkBridge.cpp	Wed Jun 25 11:56:48 2014 +0200
@@ -31,55 +31,15 @@
 
 
 
-/*=========================================================================
-
-  This file is based on portions of the following project:
-
-  Program: GDCM (Grassroots DICOM). A DICOM library
-  Module:  http://gdcm.sourceforge.net/Copyright.html
-
-Copyright (c) 2006-2011 Mathieu Malaterre
-Copyright (c) 1993-2005 CREATIS
-(CREATIS = Centre de Recherche et d'Applications en Traitement de l'Image)
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
-   this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright notice,
-   this list of conditions and the following disclaimer in the documentation
-   and/or other materials provided with the distribution.
-
- * Neither name of Mathieu Malaterre, or CREATIS, nor the names of any
-   contributors (CNRS, INSERM, UCB, Universite Lyon I), may be used to
-   endorse or promote products derived from this software without specific
-   prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-=========================================================================*/
-
-
 #include "PrecompiledHeadersServer.h"
 
 #ifndef NOMINMAX
 #define NOMINMAX
 #endif
 
+#include "Internals/DicomImageDecoder.h"
+
 #include "FromDcmtkBridge.h"
-
 #include "ToDcmtkBridge.h"
 #include "../Core/Toolbox.h"
 #include "../Core/OrthancException.h"
@@ -468,345 +428,6 @@
   }
 
 
-  static void ExtractPngImageColorPreview(std::string& result,
-                                          DicomIntegerPixelAccessor& accessor)
-  {
-    assert(accessor.GetChannelCount() == 3);
-    PngWriter w;
-
-    std::vector<uint8_t> image(accessor.GetWidth() * accessor.GetHeight() * 3, 0);
-    uint8_t* pixel = &image[0];
-
-    for (unsigned int y = 0; y < accessor.GetHeight(); y++)
-    {
-      for (unsigned int x = 0; x < accessor.GetWidth(); x++)
-      {
-        for (unsigned int c = 0; c < 3; c++, pixel++)
-        {
-          int32_t v = accessor.GetValue(x, y, c);
-          if (v < 0)
-            *pixel = 0;
-          else if (v > 255)
-            *pixel = 255;
-          else
-            *pixel = v;
-        }
-      }
-    }
-
-    w.WriteToMemory(result, accessor.GetWidth(), accessor.GetHeight(),
-                    accessor.GetWidth() * 3, PixelFormat_RGB24, &image[0]);
-  }
-
-
-  static void ExtractPngImageGrayscalePreview(std::string& result,
-                                              DicomIntegerPixelAccessor& accessor)
-  {
-    assert(accessor.GetChannelCount() == 1);
-    PngWriter w;
-
-    int32_t min, max;
-    accessor.GetExtremeValues(min, max);
-
-    std::vector<uint8_t> image(accessor.GetWidth() * accessor.GetHeight(), 0);
-    if (min != max)
-    {
-      uint8_t* pixel = &image[0];
-      for (unsigned int y = 0; y < accessor.GetHeight(); y++)
-      {
-        for (unsigned int x = 0; x < accessor.GetWidth(); x++, pixel++)
-        {
-          int32_t v = accessor.GetValue(x, y);
-          *pixel = static_cast<uint8_t>(
-            boost::math::lround(static_cast<float>(v - min) / 
-                                static_cast<float>(max - min) * 255.0f));
-        }
-      }
-    }
-
-    w.WriteToMemory(result, accessor.GetWidth(), accessor.GetHeight(),
-                    accessor.GetWidth(), PixelFormat_Grayscale8, &image[0]);
-  }
-
-
-  template <typename T>
-  static void ExtractPngImageTruncate(std::string& result,
-                                      DicomIntegerPixelAccessor& accessor,
-                                      PixelFormat format)
-  {
-    assert(accessor.GetChannelCount() == 1);
-
-    PngWriter w;
-
-    std::vector<T> image(accessor.GetWidth() * accessor.GetHeight(), 0);
-    T* pixel = &image[0];
-    for (unsigned int y = 0; y < accessor.GetHeight(); y++)
-    {
-      for (unsigned int x = 0; x < accessor.GetWidth(); x++, pixel++)
-      {
-        int32_t v = accessor.GetValue(x, y);
-        if (v < static_cast<int32_t>(std::numeric_limits<T>::min()))
-          *pixel = std::numeric_limits<T>::min();
-        else if (v > static_cast<int32_t>(std::numeric_limits<T>::max()))
-          *pixel = std::numeric_limits<T>::max();
-        else
-          *pixel = static_cast<T>(v);
-      }
-    }
-
-    w.WriteToMemory(result, accessor.GetWidth(), accessor.GetHeight(),
-                    accessor.GetWidth() * sizeof(T), format, &image[0]);
-  }
-
-
-  static bool DecodePsmctRle1(std::string& output,
-                              DcmDataset& dataset)
-  {
-    static const DicomTag tagContent(0x07a1, 0x100a);
-    static const DicomTag tagCompressionType(0x07a1, 0x1011);
-
-    DcmElement* e;
-    char* c;
-
-    // Check whether the DICOM instance contains an image encoded with
-    // the PMSCT_RLE1 scheme.
-    if (!dataset.findAndGetElement(ToDcmtkBridge::Convert(tagCompressionType), e).good() ||
-        e == NULL ||
-        !e->isaString() ||
-        !e->getString(c).good() ||
-        c == NULL ||
-        strcmp("PMSCT_RLE1", c))
-    {
-      return false;
-    }
-
-    // OK, this is a custom RLE encoding from Philips. Get the pixel
-    // data from the appropriate private DICOM tag.
-    Uint8* pixData = NULL;
-    if (!dataset.findAndGetElement(ToDcmtkBridge::Convert(tagContent), e).good() ||
-        e == NULL ||
-        e->getUint8Array(pixData) != EC_Normal)
-    {
-      return false;
-    }    
-
-    // The "unsigned" below IS VERY IMPORTANT
-    const uint8_t* inbuffer = reinterpret_cast<const uint8_t*>(pixData);
-    const size_t length = e->getLength();
-
-    /**
-     * The code below is an adaptation of a sample code for GDCM by
-     * Mathieu Malaterre (under a BSD license).
-     * http://gdcm.sourceforge.net/html/rle2img_8cxx-example.html
-     **/
-
-    // RLE pass
-    std::vector<uint8_t> temp;
-    temp.reserve(length);
-    for (size_t i = 0; i < length; i++)
-    {
-      if (inbuffer[i] == 0xa5)
-      {
-        temp.push_back(inbuffer[i+2]);
-        for (uint8_t repeat = inbuffer[i + 1]; repeat != 0; repeat--)
-        {
-          temp.push_back(inbuffer[i+2]);
-        }
-        i += 2;
-      }
-      else
-      {
-        temp.push_back(inbuffer[i]);
-      }
-    }
-
-    // Delta encoding pass
-    uint16_t delta = 0;
-    output.clear();
-    output.reserve(temp.size());
-    for (size_t i = 0; i < temp.size(); i++)
-    {
-      uint16_t value;
-
-      if (temp[i] == 0x5a)
-      {
-        uint16_t v1 = temp[i + 1];
-        uint16_t v2 = temp[i + 2];
-        value = (v2 << 8) + v1;
-        i += 2;
-      }
-      else
-      {
-        value = delta + (int8_t) temp[i];
-      }
-
-      output.push_back(value & 0xff);
-      output.push_back(value >> 8);
-      delta = value;
-    }
-
-    if (output.size() % 2)
-    {
-      output.resize(output.size() - 1);
-    }
-
-    return true;
-  }
-
-
-  void FromDcmtkBridge::ExtractPngImage(std::string& result,
-                                        DcmDataset& dataset,
-                                        unsigned int frame,
-                                        ImageExtractionMode mode)
-  {
-    // See also: http://support.dcmtk.org/wiki/dcmtk/howto/accessing-compressed-data
-
-    std::auto_ptr<DicomIntegerPixelAccessor> accessor;
-
-    DicomMap m;
-    FromDcmtkBridge::Convert(m, dataset);
-
-    std::string privateContent;
-
-    DcmElement* e;
-    if (dataset.findAndGetElement(ToDcmtkBridge::Convert(DICOM_TAG_PIXEL_DATA), e).good() &&
-        e != NULL)
-    {
-      Uint8* pixData = NULL;
-      if (e->getUint8Array(pixData) == EC_Normal)
-      {    
-        accessor.reset(new DicomIntegerPixelAccessor(m, pixData, e->getLength()));
-        accessor->SetCurrentFrame(frame);
-      }
-    }
-    else if (DecodePsmctRle1(privateContent, dataset))
-    {
-      LOG(INFO) << "The PMSCT_RLE1 decoding has succeeded";
-      Uint8* pixData = NULL;
-      if (privateContent.size() > 0)
-        pixData = reinterpret_cast<Uint8*>(&privateContent[0]);
-      accessor.reset(new DicomIntegerPixelAccessor(m, pixData, privateContent.size()));
-      accessor->SetCurrentFrame(frame);
-    }
-    
-    if (accessor.get() == NULL)
-    {
-      throw OrthancException(ErrorCode_BadFileFormat);
-    }
-
-    PixelFormat format;
-    bool supported = false;
-
-    if (accessor->GetChannelCount() == 1)
-    {
-      switch (mode)
-      {
-        case ImageExtractionMode_Preview:
-          supported = true;
-          format = PixelFormat_Grayscale8;
-          break;
-
-        case ImageExtractionMode_UInt8:
-          supported = true;
-          format = PixelFormat_Grayscale8;
-          break;
-
-        case ImageExtractionMode_UInt16:
-          supported = true;
-          format = PixelFormat_Grayscale16;
-          break;
-
-        case ImageExtractionMode_Int16:
-          supported = true;
-          format = PixelFormat_SignedGrayscale16;
-          break;
-
-        default:
-          supported = false;
-          break;
-      }
-    }
-    else if (accessor->GetChannelCount() == 3)
-    {
-      switch (mode)
-      {
-        case ImageExtractionMode_Preview:
-          supported = true;
-          format = PixelFormat_RGB24;
-          break;
-
-        default:
-          supported = false;
-          break;
-      }
-    }
-
-    if (!supported)
-    {
-      throw OrthancException(ErrorCode_NotImplemented);
-    }   
-
-    if (accessor.get() == NULL ||
-        accessor->GetWidth() == 0 ||
-        accessor->GetHeight() == 0)
-    {
-      PngWriter w;
-      w.WriteToMemory(result, 0, 0, 0, format, NULL);
-    }
-    else
-    {
-      switch (mode)
-      {
-        case ImageExtractionMode_Preview:
-          if (format == PixelFormat_Grayscale8)
-            ExtractPngImageGrayscalePreview(result, *accessor);
-          else
-            ExtractPngImageColorPreview(result, *accessor);
-          break;
-
-        case ImageExtractionMode_UInt8:
-          ExtractPngImageTruncate<uint8_t>(result, *accessor, format);
-          break;
-
-        case ImageExtractionMode_UInt16:
-          ExtractPngImageTruncate<uint16_t>(result, *accessor, format);
-          break;
-
-        case ImageExtractionMode_Int16:
-          ExtractPngImageTruncate<int16_t>(result, *accessor, format);
-          break;
-
-        default:
-          throw OrthancException(ErrorCode_NotImplemented);
-      }
-    }
-  }
-
-
-  void FromDcmtkBridge::ExtractPngImage(std::string& result,
-                                        const std::string& dicomContent,
-                                        unsigned int frame,
-                                        ImageExtractionMode mode)
-  {
-    DcmInputBufferStream is;
-    if (dicomContent.size() > 0)
-    {
-      is.setBuffer(&dicomContent[0], dicomContent.size());
-    }
-    is.setEos();
-
-    DcmFileFormat dicom;
-    if (dicom.read(is).good())
-    {
-      ExtractPngImage(result, *dicom.getDataset(), frame, mode);
-    }
-    else
-    {
-      throw OrthancException(ErrorCode_BadFileFormat);
-    }
-  }
-
-
 
   std::string FromDcmtkBridge::GetName(const DicomTag& t)
   {
--- a/OrthancServer/FromDcmtkBridge.h	Mon Jun 02 13:24:44 2014 +0200
+++ b/OrthancServer/FromDcmtkBridge.h	Wed Jun 25 11:56:48 2014 +0200
@@ -58,16 +58,6 @@
                        const std::string& path,
                        unsigned int maxStringLength = 256);
 
-    static void ExtractPngImage(std::string& result,
-                                DcmDataset& dataset,
-                                unsigned int frame,
-                                ImageExtractionMode mode);
-
-    static void ExtractPngImage(std::string& result,
-                                const std::string& dicomContent,
-                                unsigned int frame,
-                                ImageExtractionMode mode);
-
     static std::string GetName(const DicomTag& tag);
 
     static DicomTag ParseTag(const char* name);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/Internals/DicomImageDecoder.cpp	Wed Jun 25 11:56:48 2014 +0200
@@ -0,0 +1,685 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2014 Medical Physics Department, CHU 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 "../PrecompiledHeadersServer.h"
+#include "DicomImageDecoder.h"
+
+
+/*=========================================================================
+
+  This file is based on portions of the following project
+  (cf. function "DecodePsmctRle1()"):
+
+  Program: GDCM (Grassroots DICOM). A DICOM library
+  Module:  http://gdcm.sourceforge.net/Copyright.html
+
+  Copyright (c) 2006-2011 Mathieu Malaterre
+  Copyright (c) 1993-2005 CREATIS
+  (CREATIS = Centre de Recherche et d'Applications en Traitement de l'Image)
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are met:
+
+  * Redistributions of source code must retain the above copyright notice,
+  this list of conditions and the following disclaimer.
+
+  * Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+  * Neither name of Mathieu Malaterre, or CREATIS, nor the names of any
+  contributors (CNRS, INSERM, UCB, Universite Lyon I), may be used to
+  endorse or promote products derived from this software without specific
+  prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
+  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
+  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+  =========================================================================*/
+
+
+
+#include "../PrecompiledHeadersServer.h"
+#include "DicomImageDecoder.h"
+
+#include "../../Core/OrthancException.h"
+#include "../../Core/ImageFormats/ImageProcessing.h"
+#include "../../Core/ImageFormats/PngWriter.h"  // TODO REMOVE THIS
+#include "../../Core/DicomFormat/DicomIntegerPixelAccessor.h"
+#include "../ToDcmtkBridge.h"
+#include "../FromDcmtkBridge.h"
+
+#include <glog/logging.h>
+
+#include <boost/lexical_cast.hpp>
+
+#if ORTHANC_JPEG_LOSSLESS_ENABLED == 1
+#include <dcmtk/dcmjpls/djcodecd.h>
+#include <dcmtk/dcmjpls/djcparam.h>
+#include <dcmtk/dcmjpeg/djrplol.h>
+#endif
+
+
+namespace Orthanc
+{
+  static const DicomTag DICOM_TAG_CONTENT(0x07a1, 0x100a);
+  static const DicomTag DICOM_TAG_COMPRESSION_TYPE(0x07a1, 0x1011);
+
+
+  static bool IsJpegLossless(const DcmDataset& dataset)
+  {
+    // http://support.dcmtk.org/docs/dcxfer_8h-source.html
+    return (dataset.getOriginalXfer() == EXS_JPEGLSLossless ||
+            dataset.getOriginalXfer() == EXS_JPEGLSLossy);
+  }
+
+
+  static bool IsPsmctRle1(DcmDataset& dataset)
+  {
+    DcmElement* e;
+    char* c;
+
+    // Check whether the DICOM instance contains an image encoded with
+    // the PMSCT_RLE1 scheme.
+    if (!dataset.findAndGetElement(ToDcmtkBridge::Convert(DICOM_TAG_COMPRESSION_TYPE), e).good() ||
+        e == NULL ||
+        !e->isaString() ||
+        !e->getString(c).good() ||
+        c == NULL ||
+        strcmp("PMSCT_RLE1", c))
+    {
+      return false;
+    }
+    else
+    {
+      return true;
+    }
+  }
+
+
+  static bool DecodePsmctRle1(std::string& output,
+                              DcmDataset& dataset)
+  {
+    // Check whether the DICOM instance contains an image encoded with
+    // the PMSCT_RLE1 scheme.
+    if (!IsPsmctRle1(dataset))
+    {
+      return false;
+    }
+
+    // OK, this is a custom RLE encoding from Philips. Get the pixel
+    // data from the appropriate private DICOM tag.
+    Uint8* pixData = NULL;
+    DcmElement* e;
+    if (!dataset.findAndGetElement(ToDcmtkBridge::Convert(DICOM_TAG_CONTENT), e).good() ||
+        e == NULL ||
+        e->getUint8Array(pixData) != EC_Normal)
+    {
+      return false;
+    }    
+
+    // The "unsigned" below IS VERY IMPORTANT
+    const uint8_t* inbuffer = reinterpret_cast<const uint8_t*>(pixData);
+    const size_t length = e->getLength();
+
+    /**
+     * The code below is an adaptation of a sample code for GDCM by
+     * Mathieu Malaterre (under a BSD license).
+     * http://gdcm.sourceforge.net/html/rle2img_8cxx-example.html
+     **/
+
+    // RLE pass
+    std::vector<uint8_t> temp;
+    temp.reserve(length);
+    for (size_t i = 0; i < length; i++)
+    {
+      if (inbuffer[i] == 0xa5)
+      {
+        temp.push_back(inbuffer[i+2]);
+        for (uint8_t repeat = inbuffer[i + 1]; repeat != 0; repeat--)
+        {
+          temp.push_back(inbuffer[i+2]);
+        }
+        i += 2;
+      }
+      else
+      {
+        temp.push_back(inbuffer[i]);
+      }
+    }
+
+    // Delta encoding pass
+    uint16_t delta = 0;
+    output.clear();
+    output.reserve(temp.size());
+    for (size_t i = 0; i < temp.size(); i++)
+    {
+      uint16_t value;
+
+      if (temp[i] == 0x5a)
+      {
+        uint16_t v1 = temp[i + 1];
+        uint16_t v2 = temp[i + 2];
+        value = (v2 << 8) + v1;
+        i += 2;
+      }
+      else
+      {
+        value = delta + (int8_t) temp[i];
+      }
+
+      output.push_back(value & 0xff);
+      output.push_back(value >> 8);
+      delta = value;
+    }
+
+    if (output.size() % 2)
+    {
+      output.resize(output.size() - 1);
+    }
+
+    return true;
+  }
+
+
+  class DicomImageDecoder::ImageSource
+  {
+  private:
+    std::string psmct_;
+    std::auto_ptr<DicomIntegerPixelAccessor> slowAccessor_;
+    std::auto_ptr<ImageAccessor> fastAccessor_;
+
+  public:
+    void Setup(DcmDataset& dataset,
+               unsigned int frame)
+    {
+      psmct_.clear();
+      slowAccessor_.reset(NULL);
+      fastAccessor_.reset(NULL);
+
+      // See also: http://support.dcmtk.org/wiki/dcmtk/howto/accessing-compressed-data
+
+      DicomMap m;
+      FromDcmtkBridge::Convert(m, dataset);
+
+      /**
+       * Create an accessor to the raw values of the DICOM image.
+       **/
+
+      DcmElement* e;
+      if (dataset.findAndGetElement(ToDcmtkBridge::Convert(DICOM_TAG_PIXEL_DATA), e).good() &&
+          e != NULL)
+      {
+        Uint8* pixData = NULL;
+        if (e->getUint8Array(pixData) == EC_Normal)
+        {    
+          slowAccessor_.reset(new DicomIntegerPixelAccessor(m, pixData, e->getLength()));
+        }
+      }
+      else if (DecodePsmctRle1(psmct_, dataset))
+      {
+        LOG(INFO) << "The PMSCT_RLE1 decoding has succeeded";
+        Uint8* pixData = NULL;
+        if (psmct_.size() > 0)
+        {
+          pixData = reinterpret_cast<Uint8*>(&psmct_[0]);
+        }
+
+        slowAccessor_.reset(new DicomIntegerPixelAccessor(m, pixData, psmct_.size()));
+      }
+    
+      if (slowAccessor_.get() == NULL)
+      {
+        throw OrthancException(ErrorCode_BadFileFormat);
+      }
+
+      slowAccessor_->SetCurrentFrame(frame);
+
+
+      /**
+       * If possible, create a fast ImageAccessor to the image buffer.
+       **/
+
+      
+    }
+
+    unsigned int GetWidth() const
+    {
+      assert(slowAccessor_.get() != NULL);
+      return slowAccessor_->GetInformation().GetWidth();
+    }
+
+    unsigned int GetHeight() const
+    {
+      assert(slowAccessor_.get() != NULL);
+      return slowAccessor_->GetInformation().GetHeight();
+    }
+
+    unsigned int GetChannelCount() const
+    {
+      assert(slowAccessor_.get() != NULL);
+      return slowAccessor_->GetInformation().GetChannelCount();
+    }
+
+    const DicomIntegerPixelAccessor& GetAccessor() const
+    {
+      assert(slowAccessor_.get() != NULL);
+      return *slowAccessor_;
+    }
+
+    bool HasFastAccessor() const
+    {
+      return fastAccessor_.get() != NULL;
+    }
+
+    const ImageAccessor& GetFastAccessor() const
+    {
+      assert(HasFastAccessor());
+      return *fastAccessor_;
+    }
+  };
+
+
+  void DicomImageDecoder::SetupImageBuffer(ImageBuffer& target,
+                                           DcmDataset& dataset)
+  {
+    DicomMap m;
+    FromDcmtkBridge::Convert(m, dataset);
+
+    DicomImageInformation info(m);
+    PixelFormat format;
+    
+    if (!info.ExtractPixelFormat(format))
+    {
+      LOG(WARNING) << "Unsupported DICOM image: " << info.GetBitsStored() 
+                   << "bpp, " << info.GetChannelCount() << " channels, " 
+                   << (info.IsSigned() ? "signed" : "unsigned")
+                   << (info.IsPlanar() ? ", planar" : ", non-planar");
+      throw OrthancException(ErrorCode_NotImplemented);
+    }
+
+    target.SetHeight(info.GetHeight());
+    target.SetWidth(info.GetWidth());
+    target.SetFormat(format);
+  }
+
+
+  bool DicomImageDecoder::IsUncompressedImage(const DcmDataset& dataset)
+  {
+    // http://support.dcmtk.org/docs/dcxfer_8h-source.html
+    return (dataset.getOriginalXfer() == EXS_Unknown ||
+            dataset.getOriginalXfer() == EXS_LittleEndianImplicit ||
+            dataset.getOriginalXfer() == EXS_BigEndianImplicit ||
+            dataset.getOriginalXfer() == EXS_LittleEndianExplicit ||
+            dataset.getOriginalXfer() == EXS_BigEndianExplicit);
+  }
+
+
+  template <typename PixelType>
+  static void CopyPixels(ImageAccessor& target,
+                         const DicomIntegerPixelAccessor& source)
+  {
+    const PixelType minValue = std::numeric_limits<PixelType>::min();
+    const PixelType maxValue = std::numeric_limits<PixelType>::max();
+
+    for (unsigned int y = 0; y < source.GetInformation().GetHeight(); y++)
+    {
+      PixelType* pixel = reinterpret_cast<PixelType*>(target.GetRow(y));
+      for (unsigned int x = 0; x < source.GetInformation().GetWidth(); x++)
+      {
+        for (unsigned int c = 0; c < source.GetInformation().GetChannelCount(); c++, pixel++)
+        {
+          int32_t v = source.GetValue(x, y, c);
+          if (v < static_cast<int32_t>(minValue))
+          {
+            *pixel = minValue;
+          }
+          else if (v > static_cast<int32_t>(maxValue))
+          {
+            *pixel = maxValue;
+          }
+          else
+          {
+            *pixel = static_cast<PixelType>(v);
+          }
+        }
+      }
+    }
+  }
+
+
+  void DicomImageDecoder::DecodeUncompressedImage(ImageBuffer& target,
+                                                  DcmDataset& dataset,
+                                                  unsigned int frame)
+  {
+    if (!IsUncompressedImage(dataset))
+    {
+      throw OrthancException(ErrorCode_BadParameterType);
+    }
+
+    DecodeUncompressedImageInternal(target, dataset, frame);
+  }
+
+
+  void DicomImageDecoder::DecodeUncompressedImageInternal(ImageBuffer& target,
+                                                          DcmDataset& dataset,
+                                                          unsigned int frame)
+  {
+    ImageSource source;
+    source.Setup(dataset, frame);
+
+
+    /**
+     * Resize the target image.
+     **/
+
+    SetupImageBuffer(target, dataset);
+
+    if (source.GetWidth() != target.GetWidth() ||
+        source.GetHeight() != target.GetHeight())
+    {
+      throw OrthancException(ErrorCode_InternalError);
+    }
+
+
+    /**
+     * If the format of the DICOM buffer is natively supported, use a
+     * direct access to copy its values.
+     **/
+
+    ImageAccessor targetAccessor(target.GetAccessor());
+    const DicomImageInformation& info = source.GetAccessor().GetInformation();
+
+    bool fastVersionSuccess = false;
+    PixelFormat sourceFormat;
+    if (!info.IsPlanar() &&
+        info.ExtractPixelFormat(sourceFormat))
+    {
+      try
+      {
+        ImageAccessor sourceImage;
+        sourceImage.AssignReadOnly(sourceFormat, 
+                                   info.GetWidth(), 
+                                   info.GetHeight(),
+                                   info.GetWidth() * GetBytesPerPixel(sourceFormat),
+                                   source.GetAccessor().GetPixelData());                                   
+
+        ImageProcessing::Convert(targetAccessor, sourceImage);
+        ImageProcessing::ShiftRight(targetAccessor, info.GetShift());
+        fastVersionSuccess = true;
+      }
+      catch (OrthancException&)
+      {
+        // Unsupported conversion, use the slow version
+      }
+    }
+
+
+    /**
+     * Slow version : loop over the DICOM buffer, storing its value
+     * into the target image.
+     **/
+
+    if (!fastVersionSuccess)
+    {
+      switch (target.GetFormat())
+      {
+        case PixelFormat_RGB24:
+        case PixelFormat_RGBA32:
+        case PixelFormat_Grayscale8:
+          CopyPixels<uint8_t>(targetAccessor, source.GetAccessor());
+          break;
+        
+        case PixelFormat_Grayscale16:
+          CopyPixels<uint16_t>(targetAccessor, source.GetAccessor());
+          break;
+
+        case PixelFormat_SignedGrayscale16:
+          CopyPixels<int16_t>(targetAccessor, source.GetAccessor());
+          break;
+
+        default:
+          throw OrthancException(ErrorCode_InternalError);
+      }
+    }
+  }
+
+
+#if ORTHANC_JPEG_LOSSLESS_ENABLED == 1
+  void DicomImageDecoder::DecodeJpegLossless(ImageBuffer& target,
+                                             DcmDataset& dataset,
+                                             unsigned int frame)
+  {
+    if (!IsJpegLossless(dataset))
+    {
+      throw OrthancException(ErrorCode_BadParameterType);
+    }
+
+    DcmElement *element = NULL;
+    if (!dataset.findAndGetElement(ToDcmtkBridge::Convert(DICOM_TAG_PIXEL_DATA), element).good())
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+
+    DcmPixelData& pixelData = dynamic_cast<DcmPixelData&>(*element);
+    DcmPixelSequence* pixelSequence = NULL;
+    if (!pixelData.getEncapsulatedRepresentation
+        (dataset.getOriginalXfer(), NULL, pixelSequence).good())
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+
+    SetupImageBuffer(target, dataset);
+
+    ImageAccessor targetAccessor(target.GetAccessor());
+
+    /**
+     * The "DJLSLosslessDecoder" and "DJLSNearLosslessDecoder" in DCMTK
+     * are exactly the same, except for the "supportedTransferSyntax()"
+     * virtual function.
+     * http://support.dcmtk.org/docs/classDJLSDecoderBase.html
+     **/
+
+    DJLSLosslessDecoder decoder; DJLSCodecParameter parameters;
+    //DJLSNearLosslessDecoder decoder; DJLSCodecParameter parameters;
+
+    Uint32 startFragment = 0;  // Default 
+    OFString decompressedColorModel;  // Out
+    DJ_RPLossless representationParameter;
+    OFCondition c = decoder.decodeFrame(&representationParameter, pixelSequence, &parameters, 
+                                        &dataset, frame, startFragment, targetAccessor.GetBuffer(), 
+                                        targetAccessor.GetSize(), decompressedColorModel);
+
+    if (!c.good())
+    {
+      throw OrthancException(ErrorCode_InternalError);
+    }
+  }
+#endif
+
+
+
+
+  bool DicomImageDecoder::Decode(ImageBuffer& target,
+                                 DcmDataset& dataset,
+                                 unsigned int frame)
+  {
+    if (IsUncompressedImage(dataset))
+    {
+      DecodeUncompressedImage(target, dataset, frame);
+      return true;
+    }
+
+
+#if ORTHANC_JPEG_LOSSLESS_ENABLED == 1
+    if (IsJpegLossless(dataset))
+    {
+      LOG(INFO) << "Decoding a JPEG-LS image";
+      DecodeJpegLossless(target, dataset, frame);
+      return true;
+    }
+#endif
+
+
+#if ORTHANC_JPEG_ENABLED == 1
+    // TODO Implement this part to speed up JPEG decompression
+#endif
+
+
+    /**
+     * This DICOM image format is not natively supported by
+     * Orthanc. As a last resort, try and decode it through
+     * DCMTK. This will result in higher memory consumption. This is
+     * actually the second example of the following page:
+     * http://support.dcmtk.org/docs/mod_dcmjpeg.html#Examples
+     **/
+    
+    {
+      LOG(INFO) << "Using DCMTK to decode a compressed image";
+
+      std::auto_ptr<DcmDataset> converted(dynamic_cast<DcmDataset*>(dataset.clone()));
+      converted->chooseRepresentation(EXS_LittleEndianExplicit, NULL);
+
+      if (converted->canWriteXfer(EXS_LittleEndianExplicit))
+      {
+        DecodeUncompressedImageInternal(target, *converted, frame);
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+
+  bool DicomImageDecoder::DecodeAndTruncate(ImageBuffer& target,
+                                            DcmDataset& dataset,
+                                            unsigned int frame,
+                                            PixelFormat format)
+  {
+    // TODO Special case for uncompressed images
+    
+    ImageBuffer source;
+    if (!Decode(source, dataset, frame))
+    {
+      return false;
+    }
+
+    if (source.GetFormat() == format)
+    {
+      // No conversion is required, return the temporary image
+      target.AcquireOwnership(source);
+      return true;
+    }
+
+    target.SetFormat(format);
+    target.SetWidth(source.GetWidth());
+    target.SetHeight(source.GetHeight());
+
+    ImageAccessor targetAccessor(target.GetAccessor());
+    ImageAccessor sourceAccessor(source.GetAccessor());
+    ImageProcessing::Convert(targetAccessor, sourceAccessor);
+
+    return true;
+  }
+
+
+  bool DicomImageDecoder::DecodePreview(ImageBuffer& target,
+                                        DcmDataset& dataset,
+                                        unsigned int frame)
+  {
+    // TODO Special case for uncompressed images
+    
+    ImageBuffer source;
+    if (!Decode(source, dataset, frame))
+    {
+      return false;
+    }
+
+    switch (source.GetFormat())
+    {
+      case PixelFormat_RGB24:
+      {
+        // Directly return color images (RGB)
+        target.AcquireOwnership(source);
+        return true;
+      }
+
+      case PixelFormat_Grayscale8:
+      case PixelFormat_Grayscale16:
+      case PixelFormat_SignedGrayscale16:
+      {
+        // Grayscale image: Stretch its dynamics to the [0,255] range
+        target.SetFormat(PixelFormat_Grayscale8);
+        target.SetWidth(source.GetWidth());
+        target.SetHeight(source.GetHeight());
+
+        ImageAccessor targetAccessor(target.GetAccessor());
+        ImageAccessor sourceAccessor(source.GetAccessor());
+
+        int64_t a, b;
+        ImageProcessing::GetMinMaxValue(a, b, sourceAccessor);
+        
+        if (a == b)
+        {
+          ImageProcessing::Set(targetAccessor, 0);
+        }
+        else
+        {
+          ImageProcessing::ShiftScale(sourceAccessor, static_cast<float>(-a), 255.0f / static_cast<float>(b - a));
+
+          if (source.GetFormat() == PixelFormat_Grayscale8)
+          {
+            target.AcquireOwnership(source);
+          }
+          else
+          {
+            ImageProcessing::Convert(targetAccessor, sourceAccessor);
+          }
+        }
+
+        return true;
+      }
+      
+      default:
+        throw OrthancException(ErrorCode_NotImplemented);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/Internals/DicomImageDecoder.h	Wed Jun 25 11:56:48 2014 +0200
@@ -0,0 +1,81 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2014 Medical Physics Department, CHU 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 <dcmtk/dcmdata/dcfilefo.h>
+
+#include "../../Core/ImageFormats/ImageBuffer.h"
+
+namespace Orthanc
+{
+  class DicomImageDecoder
+  {
+  private:
+    class ImageSource;
+
+    static void DecodeUncompressedImageInternal(ImageBuffer& target,
+                                                DcmDataset& dataset,
+                                                unsigned int frame);
+
+    static bool IsPsmctRle1(DcmDataset& dataset);
+
+    static void SetupImageBuffer(ImageBuffer& target,
+                                 DcmDataset& dataset);
+
+    static bool IsUncompressedImage(const DcmDataset& dataset);
+
+    static void DecodeUncompressedImage(ImageBuffer& target,
+                                        DcmDataset& dataset,
+                                        unsigned int frame);
+
+#if ORTHANC_JPEG_LOSSLESS_ENABLED == 1
+    static void DecodeJpegLossless(ImageBuffer& target,
+                                   DcmDataset& dataset,
+                                   unsigned int frame);
+#endif
+
+  public:
+    static bool Decode(ImageBuffer& target,
+                       DcmDataset& dataset,
+                       unsigned int frame);
+
+    static bool DecodeAndTruncate(ImageBuffer& target,
+                                  DcmDataset& dataset,
+                                  unsigned int frame,
+                                  PixelFormat format);
+
+    static bool DecodePreview(ImageBuffer& target,
+                              DcmDataset& dataset,
+                              unsigned int frame);
+  };
+}
--- a/OrthancServer/Internals/FindScp.cpp	Mon Jun 02 13:24:44 2014 +0200
+++ b/OrthancServer/Internals/FindScp.cpp	Wed Jun 25 11:56:48 2014 +0200
@@ -100,6 +100,7 @@
       DicomFindAnswers answers_;
       DcmDataset* lastRequest_;
       const std::string* callingAETitle_;
+      bool noCroppingOfResults_;
     };
 
 
@@ -125,12 +126,12 @@
 
         try
         {
-          data.handler_->Handle(data.answers_, data.input_, *data.callingAETitle_);
+          data.noCroppingOfResults_ = data.handler_->Handle(data.answers_, data.input_, *data.callingAETitle_);
         }
         catch (OrthancException& e)
         {
           // Internal error!
-          LOG(ERROR) <<  "IFindRequestHandler Failed: " << e.What();
+          LOG(ERROR) <<  "C-FIND request handler has failed: " << e.What();
           response->DimseStatus = STATUS_FIND_Failed_UnableToProcess;
           *responseIdentifiers = NULL;   
           return;
@@ -148,12 +149,21 @@
 
       if (responseCount <= static_cast<int>(data.answers_.GetSize()))
       {
+        // There are pending results that are still to be sent
         response->DimseStatus = STATUS_Pending;
         *responseIdentifiers = ToDcmtkBridge::Convert(data.answers_.GetAnswer(responseCount - 1));
       }
+      else if (data.noCroppingOfResults_)
+      {
+        // Success: All the results have been sent
+        response->DimseStatus = STATUS_Success;
+        *responseIdentifiers = NULL;
+      }
       else
       {
-        response->DimseStatus = STATUS_Success;
+        // Success, but the results were too numerous and had to be cropped
+        LOG(WARNING) <<  "Too many results for an incoming C-FIND query";
+        response->DimseStatus = STATUS_FIND_Cancel_MatchingTerminatedDueToCancelRequest;
         *responseIdentifiers = NULL;
       }
     }
@@ -170,6 +180,7 @@
     data.lastRequest_ = NULL;
     data.handler_ = &handler;
     data.callingAETitle_ = &callingAETitle;
+    data.noCroppingOfResults_ = true;
 
     OFCondition cond = DIMSE_findProvider(assoc, presID, &msg->msg.CFindRQ, 
                                           FindScpCallback, &data,
--- a/OrthancServer/OrthancFindRequestHandler.cpp	Mon Jun 02 13:24:44 2014 +0200
+++ b/OrthancServer/OrthancFindRequestHandler.cpp	Wed Jun 25 11:56:48 2014 +0200
@@ -442,7 +442,26 @@
   }
 
 
-  void OrthancFindRequestHandler::Handle(DicomFindAnswers& answers,
+  bool OrthancFindRequestHandler::HasReachedLimit(const DicomFindAnswers& answers,
+                                                  ResourceType level) const
+  {
+    switch (level)
+    {
+      case ResourceType_Patient:
+      case ResourceType_Study:
+      case ResourceType_Series:
+        return (maxResults_ != 0 && answers.GetSize() >= maxResults_);
+
+      case ResourceType_Instance:
+        return (maxInstances_ != 0 && answers.GetSize() >= maxInstances_);
+
+      default:
+        throw OrthancException(ErrorCode_InternalError);
+    }
+  }
+
+
+  bool OrthancFindRequestHandler::Handle(DicomFindAnswers& answers,
                                          const DicomMap& input,
                                          const std::string& callingAETitle)
   {
@@ -580,6 +599,12 @@
         
           if (Matches(info, query))
           {
+            if (HasReachedLimit(answers, level))
+            {
+              // Too many results, stop before recording this new match
+              return false;
+            }
+
             AddAnswer(answers, info, query);
           }
         }
@@ -589,6 +614,8 @@
         // This resource has probably been deleted during the find request
       }
     }
+
+    return true;  // All the matching resources have been returned
   }
 }
 
--- a/OrthancServer/OrthancFindRequestHandler.h	Mon Jun 02 13:24:44 2014 +0200
+++ b/OrthancServer/OrthancFindRequestHandler.h	Wed Jun 25 11:56:48 2014 +0200
@@ -41,15 +41,42 @@
   {
   private:
     ServerContext& context_;
+    unsigned int maxResults_;
+    unsigned int maxInstances_;
+
+    bool HasReachedLimit(const DicomFindAnswers& answers,
+                         ResourceType level) const;
 
   public:
     OrthancFindRequestHandler(ServerContext& context) :
-    context_(context)
+      context_(context), 
+      maxResults_(0),
+      maxInstances_(0)
     {
     }
 
-    virtual void Handle(DicomFindAnswers& answers,
+    virtual bool Handle(DicomFindAnswers& answers,
                         const DicomMap& input,
                         const std::string& callingAETitle);
+
+    unsigned int GetMaxResults() const
+    {
+      return maxResults_;
+    }
+
+    void SetMaxResults(unsigned int results)
+    {
+      maxResults_ = results;
+    }
+
+    unsigned int GetMaxInstances() const
+    {
+      return maxInstances_;
+    }
+
+    void SetMaxInstances(unsigned int instances)
+    {
+      maxInstances_ = instances;
+    }
   };
 }
--- a/OrthancServer/OrthancInitialization.cpp	Mon Jun 02 13:24:44 2014 +0200
+++ b/OrthancServer/OrthancInitialization.cpp	Wed Jun 25 11:56:48 2014 +0200
@@ -45,6 +45,17 @@
 #include <boost/thread.hpp>
 #include <glog/logging.h>
 
+
+#if ORTHANC_JPEG_ENABLED == 1
+#include <dcmtk/dcmjpeg/djdecode.h>
+#endif
+
+
+#if ORTHANC_JPEG_LOSSLESS_ENABLED == 1
+#include <dcmtk/dcmjpls/djdecode.h>
+#endif
+
+
 namespace Orthanc
 {
   static boost::mutex globalMutex_;
@@ -182,6 +193,16 @@
     RegisterUserContentType();
 
     DicomServer::InitializeDictionary();
+
+#if ORTHANC_JPEG_LOSSLESS_ENABLED == 1
+    LOG(WARNING) << "Registering JPEG Lossless codecs";
+    DJLSDecoderRegistration::registerCodecs();    
+#endif
+
+#if ORTHANC_JPEG_ENABLED == 1
+    LOG(WARNING) << "Registering JPEG codecs";
+    DJDecoderRegistration::registerCodecs(); 
+#endif
   }
 
 
@@ -191,6 +212,16 @@
     boost::mutex::scoped_lock lock(globalMutex_);
     HttpClient::GlobalFinalize();
     configuration_.reset(NULL);
+
+#if ORTHANC_JPEG_LOSSLESS_ENABLED == 1
+    // Unregister JPEG-LS codecs
+    DJLSDecoderRegistration::cleanup();
+#endif
+
+#if ORTHANC_JPEG_ENABLED == 1
+    // Unregister JPEG codecs
+    DJDecoderRegistration::cleanup();
+#endif
   }
 
 
@@ -268,7 +299,7 @@
     {
       LOG(ERROR) << "Syntax error in the definition of modality \"" << name 
                  << "\". Please check your configuration file.";
-      throw e;
+      throw;
     }
   }
 
@@ -299,7 +330,7 @@
     {
       LOG(ERROR) << "Syntax error in the definition of peer \"" << name 
                  << "\". Please check your configuration file.";
-      throw e;
+      throw;
     }
   }
 
--- a/OrthancServer/OrthancRestApi/OrthancRestResources.cpp	Mon Jun 02 13:24:44 2014 +0200
+++ b/OrthancServer/OrthancRestApi/OrthancRestResources.cpp	Wed Jun 25 11:56:48 2014 +0200
@@ -202,9 +202,11 @@
     std::string dicomContent, png;
     context.ReadFile(dicomContent, publicId, FileContentType_Dicom);
 
+    ParsedDicomFile dicom(dicomContent);
+
     try
     {
-      FromDcmtkBridge::ExtractPngImage(png, dicomContent, frame, mode);
+      dicom.ExtractPngImage(png, frame, mode);
       call.GetOutput().AnswerBuffer(png, "image/png");
     }
     catch (OrthancException& e)
@@ -228,6 +230,39 @@
   }
 
 
+  static void GetMatlabImage(RestApi::GetCall& call)
+  {
+    ServerContext& context = OrthancRestApi::GetContext(call);
+
+    std::string frameId = call.GetUriComponent("frame", "0");
+
+    unsigned int frame;
+    try
+    {
+      frame = boost::lexical_cast<unsigned int>(frameId);
+    }
+    catch (boost::bad_lexical_cast)
+    {
+      return;
+    }
+
+    std::string publicId = call.GetUriComponent("id", "");
+    std::string dicomContent;
+    context.ReadFile(dicomContent, publicId, FileContentType_Dicom);
+
+    ParsedDicomFile dicom(dicomContent);
+    ImageBuffer buffer;
+    dicom.ExtractImage(buffer, frame);
+
+    ImageAccessor accessor(buffer.GetConstAccessor());
+
+    std::string result;
+    accessor.ToMatlabString(result);
+
+    call.GetOutput().AnswerBuffer(result, "text/plain");
+  }
+
+
 
   static void GetResourceStatistics(RestApi::GetCall& call)
   {
@@ -587,10 +622,12 @@
     Register("/instances/{id}/frames/{frame}/image-uint8", GetImage<ImageExtractionMode_UInt8>);
     Register("/instances/{id}/frames/{frame}/image-uint16", GetImage<ImageExtractionMode_UInt16>);
     Register("/instances/{id}/frames/{frame}/image-int16", GetImage<ImageExtractionMode_Int16>);
+    Register("/instances/{id}/frames/{frame}/matlab", GetMatlabImage);
     Register("/instances/{id}/preview", GetImage<ImageExtractionMode_Preview>);
     Register("/instances/{id}/image-uint8", GetImage<ImageExtractionMode_UInt8>);
     Register("/instances/{id}/image-uint16", GetImage<ImageExtractionMode_UInt16>);
     Register("/instances/{id}/image-int16", GetImage<ImageExtractionMode_Int16>);
+    Register("/instances/{id}/matlab", GetMatlabImage);
 
     Register("/patients/{id}/protected", IsProtectedPatient);
     Register("/patients/{id}/protected", SetPatientProtection);
--- a/OrthancServer/ParsedDicomFile.cpp	Mon Jun 02 13:24:44 2014 +0200
+++ b/OrthancServer/ParsedDicomFile.cpp	Wed Jun 25 11:56:48 2014 +0200
@@ -82,8 +82,10 @@
 
 #include "FromDcmtkBridge.h"
 #include "ToDcmtkBridge.h"
+#include "Internals/DicomImageDecoder.h"
 #include "../Core/Toolbox.h"
 #include "../Core/OrthancException.h"
+#include "../Core/ImageFormats/ImageBuffer.h"
 #include "../Core/ImageFormats/PngWriter.h"
 #include "../Core/Uuid.h"
 #include "../Core/DicomFormat/DicomString.h"
@@ -999,15 +1001,15 @@
                                       DicomIntegerPixelAccessor& accessor,
                                       PixelFormat format)
   {
-    assert(accessor.GetChannelCount() == 1);
+    assert(accessor.GetInformation().GetChannelCount() == 1);
 
     PngWriter w;
 
-    std::vector<T> image(accessor.GetWidth() * accessor.GetHeight(), 0);
+    std::vector<T> image(accessor.GetInformation().GetWidth() * accessor.GetInformation().GetHeight(), 0);
     T* pixel = &image[0];
-    for (unsigned int y = 0; y < accessor.GetHeight(); y++)
+    for (unsigned int y = 0; y < accessor.GetInformation().GetHeight(); y++)
     {
-      for (unsigned int x = 0; x < accessor.GetWidth(); x++, pixel++)
+      for (unsigned int x = 0; x < accessor.GetInformation().GetWidth(); x++, pixel++)
       {
         int32_t v = accessor.GetValue(x, y);
         if (v < static_cast<int32_t>(std::numeric_limits<T>::min()))
@@ -1019,8 +1021,8 @@
       }
     }
 
-    w.WriteToMemory(result, accessor.GetWidth(), accessor.GetHeight(),
-                    accessor.GetWidth() * sizeof(T), format, &image[0]);
+    w.WriteToMemory(result, accessor.GetInformation().GetWidth(), accessor.GetInformation().GetHeight(),
+                    accessor.GetInformation().GetWidth() * sizeof(T), format, &image[0]);
   }
 
 
@@ -1214,4 +1216,67 @@
       throw OrthancException(ErrorCode_InternalError);
     }    
   }
+
+  
+  void ParsedDicomFile::ExtractImage(ImageBuffer& result,
+                                     unsigned int frame)
+  {
+    DcmDataset& dataset = *pimpl_->file_->getDataset();
+
+    if (!DicomImageDecoder::Decode(result, dataset, frame))
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+  }
+
+
+  void ParsedDicomFile::ExtractImage(ImageBuffer& result,
+                                     unsigned int frame,
+                                     ImageExtractionMode mode)
+  {
+    DcmDataset& dataset = *pimpl_->file_->getDataset();
+
+    bool ok = false;
+
+    switch (mode)
+    {
+      case ImageExtractionMode_UInt8:
+        ok = DicomImageDecoder::DecodeAndTruncate(result, dataset, frame, PixelFormat_Grayscale8);
+        break;
+
+      case ImageExtractionMode_UInt16:
+        ok = DicomImageDecoder::DecodeAndTruncate(result, dataset, frame, PixelFormat_Grayscale16);
+        break;
+
+      case ImageExtractionMode_Int16:
+        ok = DicomImageDecoder::DecodeAndTruncate(result, dataset, frame, PixelFormat_SignedGrayscale16);
+        break;
+
+      case ImageExtractionMode_Preview:
+        ok = DicomImageDecoder::DecodePreview(result, dataset, frame);
+        break;
+
+      default:
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+
+    if (!ok)
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+  }
+
+
+  void ParsedDicomFile::ExtractPngImage(std::string& result,
+                                        unsigned int frame,
+                                        ImageExtractionMode mode)
+  {
+    ImageBuffer buffer;
+    ExtractImage(buffer, frame, mode);
+
+    ImageAccessor accessor(buffer.GetConstAccessor());
+    PngWriter writer;
+    writer.WriteToMemory(result, accessor);
+  }
+
 }
--- a/OrthancServer/ParsedDicomFile.h	Mon Jun 02 13:24:44 2014 +0200
+++ b/OrthancServer/ParsedDicomFile.h	Wed Jun 25 11:56:48 2014 +0200
@@ -36,6 +36,7 @@
 #include "../Core/RestApi/RestApiOutput.h"
 #include "ServerEnumerations.h"
 #include "../Core/ImageFormats/ImageAccessor.h"
+#include "../Core/ImageFormats/ImageBuffer.h"
 
 namespace Orthanc
 {
@@ -92,6 +93,17 @@
     void EmbedImage(const ImageAccessor& accessor);
 
     void EmbedImage(const std::string& dataUriScheme);
+
+    void ExtractImage(ImageBuffer& result,
+                      unsigned int frame);
+
+    void ExtractImage(ImageBuffer& result,
+                      unsigned int frame,
+                      ImageExtractionMode mode);
+
+    void ExtractPngImage(std::string& result,
+                         unsigned int frame,
+                         ImageExtractionMode mode);
   };
 
 }
--- a/OrthancServer/ServerContext.cpp	Mon Jun 02 13:24:44 2014 +0200
+++ b/OrthancServer/ServerContext.cpp	Wed Jun 25 11:56:48 2014 +0200
@@ -286,7 +286,7 @@
         LogMissingRequiredTag(dicomSummary);
       }
 
-      throw e;
+      throw;
     }
   }
 
--- a/OrthancServer/main.cpp	Mon Jun 02 13:24:44 2014 +0200
+++ b/OrthancServer/main.cpp	Wed Jun 25 11:56:48 2014 +0200
@@ -98,7 +98,32 @@
 
   virtual IFindRequestHandler* ConstructFindRequestHandler()
   {
-    return new OrthancFindRequestHandler(context_);
+    std::auto_ptr<OrthancFindRequestHandler> result(new OrthancFindRequestHandler(context_));
+
+    result->SetMaxResults(Configuration::GetGlobalIntegerParameter("LimitFindResults", 0));
+    result->SetMaxInstances(Configuration::GetGlobalIntegerParameter("LimitFindInstances", 0));
+
+    if (result->GetMaxResults() == 0)
+    {
+      LOG(INFO) << "No limit on the number of C-FIND results at the Patient, Study and Series levels";
+    }
+    else
+    {
+      LOG(INFO) << "Maximum " << result->GetMaxResults() 
+                << " results for C-FIND queries at the Patient, Study and Series levels";
+    }
+
+    if (result->GetMaxInstances() == 0)
+    {
+      LOG(INFO) << "No limit on the number of C-FIND results at the Instance level";
+    }
+    else
+    {
+      LOG(INFO) << "Maximum " << result->GetMaxInstances() 
+                << " instances will be returned for C-FIND queries at the Instance level";
+    }
+
+    return result.release();
   }
 
   virtual IMoveRequestHandler* ConstructMoveRequestHandler()
--- a/Resources/CMake/BoostConfiguration.cmake	Mon Jun 02 13:24:44 2014 +0200
+++ b/Resources/CMake/BoostConfiguration.cmake	Wed Jun 25 11:56:48 2014 +0200
@@ -8,7 +8,7 @@
   #set(Boost_USE_STATIC_LIBS ON)
 
   find_package(Boost
-    COMPONENTS filesystem thread system date_time regex)
+    COMPONENTS filesystem thread system date_time regex locale)
 
   if (NOT Boost_FOUND)
     message(FATAL_ERROR "Unable to locate Boost on this system")
@@ -53,7 +53,10 @@
     )
 
   set(BOOST_SOURCES)
-  if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
+
+  if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR
+      ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" OR
+      ${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD")
     list(APPEND BOOST_SOURCES
       ${BOOST_SOURCES_DIR}/libs/thread/src/pthread/once.cpp
       ${BOOST_SOURCES_DIR}/libs/thread/src/pthread/thread.cpp
@@ -76,10 +79,17 @@
     add_definitions(
       -DBOOST_LOCALE_WITH_WCONV=1
       )
+
   else()
     message(FATAL_ERROR "Support your platform here")
   endif()
 
+  if (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
+    list(APPEND BOOST_SOURCES
+      ${BOOST_SOURCES_DIR}/libs/filesystem/src/utf8_codecvt_facet.cpp
+      )
+  endif()
+
   aux_source_directory(${BOOST_SOURCES_DIR}/libs/regex/src BOOST_REGEX_SOURCES)
 
   list(APPEND BOOST_SOURCES
@@ -120,6 +130,6 @@
   source_group(ThirdParty\\Boost REGULAR_EXPRESSION ${BOOST_SOURCES_DIR}/.*)
 else()
   add_definitions(
-    -DBOOST_HAS_LOCALE=0
+    -DBOOST_HAS_LOCALE=1
     )
 endif()
--- a/Resources/CMake/Compiler.cmake	Mon Jun 02 13:24:44 2014 +0200
+++ b/Resources/CMake/Compiler.cmake	Wed Jun 25 11:56:48 2014 +0200
@@ -37,7 +37,8 @@
 endif()
 
 
-if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
+if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR
+    ${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD")
   if ("${CMAKE_SYSTEM_VERSION}" STREQUAL "LinuxStandardBase")
     SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --lsb-target-version=${LSB_TARGET_VERSION} -I${LSB_PATH}/include")
     SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --lsb-target-version=${LSB_TARGET_VERSION} -nostdinc++ -I${LSB_PATH}/include -I${LSB_PATH}/include/c++ -I${LSB_PATH}/include/c++/backward -fpermissive")
@@ -69,6 +70,12 @@
     SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--allow-multiple-definition -static-libgcc -static-libstdc++")
   endif()
 
+elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
+  add_definitions(
+    -D_XOPEN_SOURCE=1
+    )
+  link_libraries(iconv)
+
 endif()
 
 
--- a/Resources/CMake/DcmtkConfiguration.cmake	Mon Jun 02 13:24:44 2014 +0200
+++ b/Resources/CMake/DcmtkConfiguration.cmake	Wed Jun 25 11:56:48 2014 +0200
@@ -12,7 +12,6 @@
 endif()
 
 
-
 if (STATIC_BUILD OR NOT USE_SYSTEM_DCMTK)
   SET(DCMTK_VERSION_NUMBER 360)
   SET(DCMTK_SOURCES_DIR ${CMAKE_BINARY_DIR}/dcmtk-3.6.0)
@@ -49,9 +48,47 @@
   AUX_SOURCE_DIRECTORY(${DCMTK_SOURCES_DIR}/dcmdata/libsrc DCMTK_SOURCES)
   AUX_SOURCE_DIRECTORY(${DCMTK_SOURCES_DIR}/ofstd/libsrc DCMTK_SOURCES)
 
+
+  if (ENABLE_JPEG)
+    AUX_SOURCE_DIRECTORY(${DCMTK_SOURCES_DIR}/dcmjpeg/libsrc DCMTK_SOURCES)
+    AUX_SOURCE_DIRECTORY(${DCMTK_SOURCES_DIR}/dcmjpeg/libijg8 DCMTK_SOURCES)
+    AUX_SOURCE_DIRECTORY(${DCMTK_SOURCES_DIR}/dcmjpeg/libijg12 DCMTK_SOURCES)
+    AUX_SOURCE_DIRECTORY(${DCMTK_SOURCES_DIR}/dcmjpeg/libijg16 DCMTK_SOURCES)
+    include_directories(
+      ${DCMTK_SOURCES_DIR}/dcmjpeg/include
+      ${DCMTK_SOURCES_DIR}/dcmjpeg/libijg8
+      ${DCMTK_SOURCES_DIR}/dcmjpeg/libijg12
+      ${DCMTK_SOURCES_DIR}/dcmjpeg/libijg16
+      ${DCMTK_SOURCES_DIR}/dcmimgle/include
+      )
+    list(REMOVE_ITEM DCMTK_SOURCES 
+      ${DCMTK_SOURCES_DIR}/dcmjpeg/libsrc/ddpiimpl.cc
+      )
+  endif()
+
+
+  if (ENABLE_JPEG_LOSSLESS)
+    AUX_SOURCE_DIRECTORY(${DCMTK_SOURCES_DIR}/dcmjpls/libsrc DCMTK_SOURCES)
+    AUX_SOURCE_DIRECTORY(${DCMTK_SOURCES_DIR}/dcmjpls/libcharls DCMTK_SOURCES)
+    include_directories(
+      ${DCMTK_SOURCES_DIR}/dcmjpeg/include
+      ${DCMTK_SOURCES_DIR}/dcmjpls/include
+      ${DCMTK_SOURCES_DIR}/dcmjpls/libcharls
+      )
+    list(REMOVE_ITEM DCMTK_SOURCES 
+      ${DCMTK_SOURCES_DIR}/dcmjpls/libsrc/djcodece.cc
+      )
+    list(APPEND DCMTK_SOURCES 
+      ${DCMTK_SOURCES_DIR}/dcmjpeg/libsrc/djrplol.cc
+      )
+  endif()
+
+
   # Source for the logging facility of DCMTK
   AUX_SOURCE_DIRECTORY(${DCMTK_SOURCES_DIR}/oflog/libsrc DCMTK_SOURCES)
-  if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
+  if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR
+      ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" OR
+      ${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD")
     list(REMOVE_ITEM DCMTK_SOURCES 
       ${DCMTK_SOURCES_DIR}/oflog/libsrc/windebap.cc
       ${DCMTK_SOURCES_DIR}/oflog/libsrc/winsock.cc
--- a/Resources/CMake/GoogleLogConfiguration.cmake	Mon Jun 02 13:24:44 2014 +0200
+++ b/Resources/CMake/GoogleLogConfiguration.cmake	Wed Jun 25 11:56:48 2014 +0200
@@ -28,7 +28,9 @@
   set(ac_google_start_namespace "namespace google {")
   set(ac_google_end_namespace "}")
 
-  if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
+  if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR
+      ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" OR
+      ${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD")
     set(ac_cv_have_unistd_h 1)
     set(ac_cv_have_stdint_h 1)
     set(ac_cv_have_systypes_h 0)
@@ -83,13 +85,21 @@
       )
   endif()
 
-  if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
+  if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR
+      ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" OR
+      ${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD")
     if ("${CMAKE_SYSTEM_VERSION}" STREQUAL "LinuxStandardBase")
       # Install the specific configuration for LSB SDK
       configure_file(
         ${CMAKE_SOURCE_DIR}/Resources/CMake/GoogleLogConfigurationLSB.h
         ${GOOGLE_LOG_SOURCES_DIR}/src/config.h
         COPYONLY)
+    elseif ("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin")
+      # Install the specific configuration for Mac OS
+      configure_file(
+        ${CMAKE_SOURCE_DIR}/Resources/CMake/GoogleLogConfigurationDarwin.h
+        ${GOOGLE_LOG_SOURCES_DIR}/src/config.h
+        COPYONLY)
     else()
       configure_file(
         ${CMAKE_SOURCE_DIR}/Resources/CMake/GoogleLogConfiguration.h
@@ -130,13 +140,10 @@
       # This is a patch for MinGW64
       add_definitions(-D_TIME_H__S=1)
     endif()
-
   endif()
- 
-
 
   add_library(GoogleLog STATIC ${GOOGLE_LOG_SOURCES})
-  link_libraries(GoogleLog)
+  set(STATIC_GOOGLE_LOG GoogleLog)
 
 else()
   CHECK_INCLUDE_FILE_CXX(glog/logging.h HAVE_GOOGLE_LOG_H)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Resources/CMake/GoogleLogConfigurationDarwin.h	Wed Jun 25 11:56:48 2014 +0200
@@ -0,0 +1,175 @@
+/* src/config.h.  Generated from config.h.in by configure.  */
+/* src/config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Namespace for Google classes */
+#define GOOGLE_NAMESPACE google
+
+/* Define if you have the `dladdr' function */
+/* #undef HAVE_DLADDR */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <execinfo.h> header file. */
+#define HAVE_EXECINFO_H 1
+
+/* Define if you have the `fcntl' function */
+#define HAVE_FCNTL 1
+
+/* Define to 1 if you have the <glob.h> header file. */
+#define HAVE_GLOB_H 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the `pthread' library (-lpthread). */
+#define HAVE_LIBPTHREAD 1
+
+/* Define to 1 if you have the <libunwind.h> header file. */
+/* #undef HAVE_LIBUNWIND_H */
+
+/* define if you have google gflags library */
+/* #undef HAVE_LIB_GFLAGS */
+
+/* define if you have google gmock library */
+/* #undef HAVE_LIB_GMOCK */
+
+/* define if you have google gtest library */
+/* #undef HAVE_LIB_GTEST */
+
+/* define if you have libunwind */
+/* #undef HAVE_LIB_UNWIND */
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* define if the compiler implements namespaces */
+#define HAVE_NAMESPACES 1
+
+/* Define if you have POSIX threads libraries and header files. */
+#define HAVE_PTHREAD 1
+
+/* Define to 1 if you have the <pwd.h> header file. */
+#define HAVE_PWD_H 1
+
+/* define if the compiler implements pthread_rwlock_* */
+#define HAVE_RWLOCK 1
+
+/* Define if you have the `sigaltstack' function */
+#define HAVE_SIGALTSTACK 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <syscall.h> header file. */
+/* #undef HAVE_SYSCALL_H */
+
+/* Define to 1 if you have the <syslog.h> header file. */
+#define HAVE_SYSLOG_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+/* #define HAVE_SYS_STAT_H 1 */
+
+/* Define to 1 if you have the <sys/syscall.h> header file. */
+#define HAVE_SYS_SYSCALL_H 1
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#define HAVE_SYS_TIME_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+/* #define HAVE_SYS_TYPES_H 1 */
+
+/* Define to 1 if you have the <sys/ucontext.h> header file. */
+/* #define HAVE_SYS_UCONTEXT_H 1 */
+
+/* Define to 1 if you have the <sys/utsname.h> header file. */
+#define HAVE_SYS_UTSNAME_H 1
+
+/* Define to 1 if you have the <ucontext.h> header file. */
+#define HAVE_UCONTEXT_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* define if the compiler supports using expression for operator */
+#define HAVE_USING_OPERATOR 1
+
+/* define if your compiler has __attribute__ */
+#define HAVE___ATTRIBUTE__ 1
+
+/* define if your compiler has __builtin_expect */
+#define HAVE___BUILTIN_EXPECT 1
+
+/* define if your compiler has __sync_val_compare_and_swap */
+#define HAVE___SYNC_VAL_COMPARE_AND_SWAP 1
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+   */
+#define LT_OBJDIR ".libs/"
+
+/* Name of package */
+#define PACKAGE "glog"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "opensource@google.com"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "glog"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "glog 0.3.2"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "glog"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "0.3.2"
+
+/* How to access the PC from a struct ucontext */
+/*#include <ucontext.h>
+#include <sys/ucontext.h>
+#ifdef REG_RIP
+#define PC_FROM_UCONTEXT uc_mcontext.gregs[REG_RIP]
+#else
+#undef PC_FROM_UCONTEXT
+#endif*/
+
+// This is required for older versions of Linux
+#undef PC_FROM_UCONTEXT
+
+/* Define to necessary symbol if this constant uses a non-standard name on
+   your system. */
+/* #undef PTHREAD_CREATE_JOINABLE */
+
+/* The size of `void *', as computed by sizeof. */
+#define SIZEOF_VOID_P 8
+
+/* Define to 1 if you have the ANSI C header files. */
+/* #undef STDC_HEADERS */
+
+/* the namespace where STL code like vector<> is defined */
+#define STL_NAMESPACE std
+
+/* location of source code */
+#define TEST_SRC_DIR "."
+
+/* Version number of package */
+#define VERSION "0.3.2"
+
+/* Stops putting the code inside the Google namespace */
+#define _END_GOOGLE_NAMESPACE_ }
+
+/* Puts following code inside the Google namespace */
+#define _START_GOOGLE_NAMESPACE_ namespace google {
--- a/Resources/CMake/LibCurlConfiguration.cmake	Mon Jun 02 13:24:44 2014 +0200
+++ b/Resources/CMake/LibCurlConfiguration.cmake	Wed Jun 25 11:56:48 2014 +0200
@@ -40,7 +40,9 @@
       )
   endif()
 
-  if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
+  if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR
+      ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin" OR
+      ${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD")
     if ("${CMAKE_SIZEOF_VOID_P}" EQUAL "8")
       SET(TMP_OS "x86_64")
     else()
--- a/Resources/CMake/LuaConfiguration.cmake	Mon Jun 02 13:24:44 2014 +0200
+++ b/Resources/CMake/LuaConfiguration.cmake	Wed Jun 25 11:56:48 2014 +0200
@@ -51,7 +51,7 @@
     )
 
   add_library(Lua STATIC ${LUA_SOURCES})
-  link_libraries(Lua)
+  set(STATIC_LUA Lua)
 
   source_group(ThirdParty\\Lua REGULAR_EXPRESSION ${LUA_SOURCES_DIR}/.*)
 
--- a/Resources/CMake/MongooseConfiguration.cmake	Mon Jun 02 13:24:44 2014 +0200
+++ b/Resources/CMake/MongooseConfiguration.cmake	Wed Jun 25 11:56:48 2014 +0200
@@ -24,7 +24,8 @@
     add_definitions(
       -DNO_SSL_DL=1
       )
-    if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
+    if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR
+        ${CMAKE_SYSTEM_NAME} STREQUAL "kFreeBSD")
       link_libraries(dl)
     endif()
 
--- a/Resources/Configuration.json	Mon Jun 02 13:24:44 2014 +0200
+++ b/Resources/Configuration.json	Wed Jun 25 11:56:48 2014 +0200
@@ -160,5 +160,14 @@
   // will be computed and stored in the Orthanc database. This
   // information can be used to detect disk corruption, at the price
   // of a small performance overhead.
-  "StoreMD5ForAttachments" : true
+  "StoreMD5ForAttachments" : true,
+
+  // The maximum number of results for a single C-FIND request at the
+  // Patient, Study or Series level. Setting this option to "0" means
+  // no limit.
+  "LimitFindResults" : 0,
+
+  // The maximum number of results for a single C-FIND request at the
+  // Instance level. Setting this option to "0" means no limit.
+  "LimitFindInstances" : 0
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/UnitTestsSources/ImageProcessingTests.cpp	Wed Jun 25 11:56:48 2014 +0200
@@ -0,0 +1,78 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2014 Medical Physics Department, CHU 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 "PrecompiledHeadersUnitTests.h"
+#include "gtest/gtest.h"
+
+#include "../Core/DicomFormat/DicomImageInformation.h"
+#include "../Core/ImageFormats/ImageBuffer.h"
+#include "../Core/ImageFormats/ImageProcessing.h"
+
+using namespace Orthanc;
+
+
+TEST(DicomImageInformation, ExtractPixelFormat1)
+{
+  // Cardiac/MR*
+  DicomMap m;
+  m.SetValue(DICOM_TAG_ROWS, "24");
+  m.SetValue(DICOM_TAG_COLUMNS, "16");
+  m.SetValue(DICOM_TAG_BITS_ALLOCATED, "16");
+  m.SetValue(DICOM_TAG_SAMPLES_PER_PIXEL, "1");
+  m.SetValue(DICOM_TAG_BITS_STORED, "12");
+  m.SetValue(DICOM_TAG_HIGH_BIT, "11");
+  m.SetValue(DICOM_TAG_PIXEL_REPRESENTATION, "0");
+
+  DicomImageInformation info(m);
+  PixelFormat format;
+  ASSERT_TRUE(info.ExtractPixelFormat(format));
+  ASSERT_EQ(PixelFormat_Grayscale16, format);
+}
+
+
+TEST(DicomImageInformation, ExtractPixelFormat2)
+{
+  // Delphine CT
+  DicomMap m;
+  m.SetValue(DICOM_TAG_ROWS, "24");
+  m.SetValue(DICOM_TAG_COLUMNS, "16");
+  m.SetValue(DICOM_TAG_BITS_ALLOCATED, "16");
+  m.SetValue(DICOM_TAG_SAMPLES_PER_PIXEL, "1");
+  m.SetValue(DICOM_TAG_BITS_STORED, "16");
+  m.SetValue(DICOM_TAG_HIGH_BIT, "15");
+  m.SetValue(DICOM_TAG_PIXEL_REPRESENTATION, "1");
+
+  DicomImageInformation info(m);
+  PixelFormat format;
+  ASSERT_TRUE(info.ExtractPixelFormat(format));
+  ASSERT_EQ(PixelFormat_SignedGrayscale16, format);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/UnitTestsSources/JpegLossless.cpp	Wed Jun 25 11:56:48 2014 +0200
@@ -0,0 +1,54 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2014 Medical Physics Department, CHU 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 "PrecompiledHeadersUnitTests.h"
+#include "gtest/gtest.h"
+
+#include "../OrthancServer/Internals/DicomImageDecoder.h"
+
+#if ORTHANC_JPEG_LOSSLESS_ENABLED == 1
+
+#include <dcmtk/dcmdata/dcfilefo.h>
+
+#include "../OrthancServer/ParsedDicomFile.h"
+#include "../Core/OrthancException.h"
+#include "../Core/ImageFormats/ImageBuffer.h"
+#include "../Core/ImageFormats/PngWriter.h"
+
+using namespace Orthanc;
+
+
+
+// TODO Write a test
+
+
+#endif
--- a/UnitTestsSources/SQLite.cpp	Mon Jun 02 13:24:44 2014 +0200
+++ b/UnitTestsSources/SQLite.cpp	Wed Jun 25 11:56:48 2014 +0200
@@ -316,7 +316,7 @@
     ASSERT_EQ(42ll, s.ColumnInt64(1));
     ASSERT_TRUE(s.Step());
     ASSERT_EQ(SQLite::COLUMN_TYPE_FLOAT, s.GetColumnType(1));
-    ASSERT_FLOAT_EQ(42.5, s.ColumnDouble(1));
+    ASSERT_DOUBLE_EQ(42.5, s.ColumnDouble(1));
     ASSERT_TRUE(s.Step());
     ASSERT_EQ(SQLite::COLUMN_TYPE_TEXT, s.GetColumnType(1));
     ASSERT_EQ("Hello", s.ColumnString(1));
--- a/UnitTestsSources/UnitTestsMain.cpp	Mon Jun 02 13:24:44 2014 +0200
+++ b/UnitTestsSources/UnitTestsMain.cpp	Wed Jun 25 11:56:48 2014 +0200
@@ -595,7 +595,10 @@
 #if defined(_WIN32)
   ASSERT_EQ(Endianness_Little, Toolbox::DetectEndianness());
 
-#elif defined(__linux)
+#elif defined(__APPLE__)
+  ASSERT_EQ(Endianness_Little, Toolbox::DetectEndianness());
+
+#elif defined(__linux) || defined(__FreeBSD_kernel__)
 
 #if !defined(__BYTE_ORDER)
 #  error Support your platform here