changeset 866:54d2c5df6760

integration jpeg -> mainline
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 10 Jun 2014 17:28:04 +0200
parents a4d2be5154a9 (current diff) f2841a9e04cf (diff)
children 27b2d377c70e
files
diffstat 24 files changed, 2335 insertions(+), 678 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Mon Jun 02 13:01:02 2014 +0200
+++ b/CMakeLists.txt	Tue Jun 10 17:28:04 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,8 @@
   UnitTestsSources/Lua.cpp
   UnitTestsSources/MultiThreading.cpp
   UnitTestsSources/UnitTestsMain.cpp
+  UnitTestsSources/ImageProcessingTests.cpp
+  UnitTestsSources/JpegLossless.cpp
   )
 
 
@@ -201,6 +208,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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/DicomFormat/DicomImageInformation.cpp	Tue Jun 10 17:28:04 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	Tue Jun 10 17:28:04 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:01:02 2014 +0200
+++ b/Core/DicomFormat/DicomIntegerPixelAccessor.cpp	Tue Jun 10 17:28:04 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:01:02 2014 +0200
+++ b/Core/DicomFormat/DicomIntegerPixelAccessor.h	Tue Jun 10 17:28:04 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:01:02 2014 +0200
+++ b/Core/Enumerations.h	Tue Jun 10 17:28:04 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:01:02 2014 +0200
+++ b/Core/ImageFormats/ImageAccessor.cpp	Tue Jun 10 17:28:04 2014 +0200
@@ -36,6 +36,8 @@
 #include "../OrthancException.h"
 
 #include <stdint.h>
+#include <cassert>
+#include <glog/logging.h>
 
 namespace Orthanc
 {
@@ -43,6 +45,7 @@
   {
     if (readOnly_)
     {
+      LOG(ERROR) << "Trying to write on a read-only image";
       throw OrthancException(ErrorCode_ReadOnly);
     }
 
@@ -67,6 +70,7 @@
   {
     if (readOnly_)
     {
+      LOG(ERROR) << "Trying to write on a read-only image";
       throw OrthancException(ErrorCode_ReadOnly);
     }
 
@@ -104,6 +108,8 @@
     height_ = height;
     pitch_ = pitch;
     buffer_ = const_cast<void*>(buffer);
+
+    assert(GetBytesPerPixel(format_) * width_ <= pitch_);
   }
 
 
@@ -119,5 +125,7 @@
     height_ = height;
     pitch_ = pitch;
     buffer_ = buffer;
+
+    assert(GetBytesPerPixel(format_) * width_ <= pitch_);
   }
 }
--- a/Core/ImageFormats/ImageAccessor.h	Mon Jun 02 13:01:02 2014 +0200
+++ b/Core/ImageFormats/ImageAccessor.h	Tue Jun 10 17:28:04 2014 +0200
@@ -77,6 +77,11 @@
       return pitch_;
     }
 
+    unsigned int GetSize() const
+    {
+      return GetHeight() * GetPitch();
+    }
+
     const void* GetConstBuffer() const
     {
       return buffer_;
--- a/Core/ImageFormats/ImageBuffer.cpp	Mon Jun 02 13:01:02 2014 +0200
+++ b/Core/ImageFormats/ImageBuffer.cpp	Tue Jun 10 17:28:04 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:01:02 2014 +0200
+++ b/Core/ImageFormats/ImageBuffer.h	Tue Jun 10 17:28:04 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	Tue Jun 10 17:28:04 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 = maxValue;
+        }
+        else if (v < minValue)
+        {
+          *p = minValue;
+        }
+        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 = maxValue;
+        }
+        else if (v < minValue)
+        {
+          *p = minValue;
+        }
+        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	Tue Jun 10 17:28:04 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:01:02 2014 +0200
+++ b/Core/ImageFormats/PngWriter.h	Tue Jun 10 17:28:04 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/OrthancException.cpp	Mon Jun 02 13:01:02 2014 +0200
+++ b/Core/OrthancException.cpp	Tue Jun 10 17:28:04 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/LinuxCompilation.txt	Mon Jun 02 13:01:02 2014 +0200
+++ b/LinuxCompilation.txt	Tue Jun 10 17:28:04 2014 +0200
@@ -134,13 +134,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
 
 
--- a/OrthancCppClient/SharedLibrary/AUTOGENERATED/OrthancCppClient.h	Mon Jun 02 13:01:02 2014 +0200
+++ b/OrthancCppClient/SharedLibrary/AUTOGENERATED/OrthancCppClient.h	Tue Jun 10 17:28:04 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/OrthancServer/FromDcmtkBridge.cpp	Mon Jun 02 13:01:02 2014 +0200
+++ b/OrthancServer/FromDcmtkBridge.cpp	Tue Jun 10 17:28:04 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,318 +428,44 @@
   }
 
 
-  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;
+    ImageBuffer tmp;
+    bool ok = false;
 
-    DicomMap m;
-    FromDcmtkBridge::Convert(m, dataset);
-
-    std::string privateContent;
-
-    DcmElement* e;
-    if (dataset.findAndGetElement(ToDcmtkBridge::Convert(DICOM_TAG_PIXEL_DATA), e).good() &&
-        e != NULL)
+    switch (mode)
     {
-      Uint8* pixData = NULL;
-      if (e->getUint8Array(pixData) == EC_Normal)
-      {    
-        accessor.reset(new DicomIntegerPixelAccessor(m, pixData, e->getLength()));
-        accessor->SetCurrentFrame(frame);
-      }
+      case ImageExtractionMode_UInt8:
+        ok = DicomImageDecoder::DecodeAndTruncate(tmp, dataset, frame, PixelFormat_Grayscale8);
+        break;
+
+      case ImageExtractionMode_UInt16:
+        ok = DicomImageDecoder::DecodeAndTruncate(tmp, dataset, frame, PixelFormat_Grayscale16);
+        break;
+
+      case ImageExtractionMode_Int16:
+        ok = DicomImageDecoder::DecodeAndTruncate(tmp, dataset, frame, PixelFormat_SignedGrayscale16);
+        break;
+
+      case ImageExtractionMode_Preview:
+        ok = DicomImageDecoder::DecodePreview(tmp, dataset, frame);
+        break;
+
+      default:
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
     }
-    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)
+
+    if (!ok)
     {
       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);
-      }
-    }
+    ImageAccessor accessor(tmp.GetAccessor());
+    PngWriter writer;
+    writer.WriteToMemory(result, accessor);
   }
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancServer/Internals/DicomImageDecoder.cpp	Tue Jun 10 17:28:04 2014 +0200
@@ -0,0 +1,681 @@
+/**
+ * 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 "../../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
+{
+  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 (DicomImageDecoder::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_;
+    }
+  };
+
+
+  static const DicomTag DICOM_TAG_CONTENT(0x07a1, 0x100a);
+  static const DicomTag DICOM_TAG_COMPRESSION_TYPE(0x07a1, 0x1011);
+
+  bool DicomImageDecoder::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;
+    }
+  }
+
+
+  bool DicomImageDecoder::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;
+  }
+
+
+  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::IsJpegLossless(const DcmDataset& dataset)
+  {
+    // http://support.dcmtk.org/docs/dcxfer_8h-source.html
+    return (dataset.getOriginalXfer() == EXS_JPEGLSLossless ||
+            dataset.getOriginalXfer() == EXS_JPEGLSLossy);
+  }
+
+
+  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, -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	Tue Jun 10 17:28:04 2014 +0200
@@ -0,0 +1,87 @@
+/**
+ * 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);
+
+  public:   // TODO SWITCH TO PRIVATE
+    static bool DecodePsmctRle1(std::string& output,
+                                DcmDataset& dataset);
+
+  public:
+    static bool IsUncompressedImage(const DcmDataset& dataset);
+
+    static bool IsJpegLossless(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
+
+    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/OrthancInitialization.cpp	Mon Jun 02 13:01:02 2014 +0200
+++ b/OrthancServer/OrthancInitialization.cpp	Tue Jun 10 17:28:04 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
   }
 
 
--- a/OrthancServer/ParsedDicomFile.cpp	Mon Jun 02 13:01:02 2014 +0200
+++ b/OrthancServer/ParsedDicomFile.cpp	Tue Jun 10 17:28:04 2014 +0200
@@ -999,15 +999,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 +1019,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]);
   }
 
 
--- a/Resources/CMake/DcmtkConfiguration.cmake	Mon Jun 02 13:01:02 2014 +0200
+++ b/Resources/CMake/DcmtkConfiguration.cmake	Tue Jun 10 17:28:04 2014 +0200
@@ -1,151 +1,187 @@
-# Lookup for DICOM dictionaries, if none is specified by the user
-if (DCMTK_DICTIONARY_DIR STREQUAL "")
-  find_path(DCMTK_DICTIONARY_DIR_AUTO dicom.dic
-    /usr/share/dcmtk
-    /usr/share/libdcmtk2
-    )
-
-  message("Autodetected path to the DICOM dictionaries: ${DCMTK_DICTIONARY_DIR_AUTO}")
-  add_definitions(-DDCMTK_DICTIONARY_DIR="${DCMTK_DICTIONARY_DIR_AUTO}")
-else()
-  add_definitions(-DDCMTK_DICTIONARY_DIR="${DCMTK_DICTIONARY_DIR}")
-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)
-  DownloadPackage(
-    "219ad631b82031806147e4abbfba4fa4"
-    "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/dcmtk-3.6.0.zip" 
-    "${DCMTK_SOURCES_DIR}")
-
-  IF(CMAKE_CROSSCOMPILING)
-    SET(C_CHAR_UNSIGNED 1 CACHE INTERNAL "Whether char is unsigned.")
-  ENDIF()
-  SET(DCMTK_SOURCE_DIR ${CMAKE_BINARY_DIR}/dcmtk-3.6.0)
-  include(${DCMTK_SOURCES_DIR}/CMake/CheckFunctionWithHeaderExists.cmake)
-  include(${DCMTK_SOURCES_DIR}/CMake/GenerateDCMTKConfigure.cmake)
-
-  if ("${CMAKE_SYSTEM_VERSION}" STREQUAL "LinuxStandardBase")
-    set(HAVE_SSTREAM 1)
-    set(HAVE_PROTOTYPE_BZERO 1)
-    set(HAVE_PROTOTYPE_GETHOSTNAME 1)
-    set(HAVE_PROTOTYPE_GETSOCKOPT 1)
-    set(HAVE_PROTOTYPE_SETSOCKOPT 1)
-    set(HAVE_PROTOTYPE_CONNECT 1)
-    set(HAVE_PROTOTYPE_BIND 1)
-    set(HAVE_PROTOTYPE_ACCEPT 1)
-    set(HAVE_PROTOTYPE_SETSOCKNAME 1)
-    set(HAVE_PROTOTYPE_GETSOCKNAME 1)
-  endif()
-
-  CONFIGURE_FILE(
-    ${DCMTK_SOURCES_DIR}/CMake/osconfig.h.in
-    ${DCMTK_SOURCES_DIR}/config/include/dcmtk/config/osconfig.h)
-
-  AUX_SOURCE_DIRECTORY(${DCMTK_SOURCES_DIR}/dcmnet/libsrc DCMTK_SOURCES)
-  AUX_SOURCE_DIRECTORY(${DCMTK_SOURCES_DIR}/dcmdata/libsrc DCMTK_SOURCES)
-  AUX_SOURCE_DIRECTORY(${DCMTK_SOURCES_DIR}/ofstd/libsrc DCMTK_SOURCES)
-
-  # Source for the logging facility of DCMTK
-  AUX_SOURCE_DIRECTORY(${DCMTK_SOURCES_DIR}/oflog/libsrc DCMTK_SOURCES)
-  if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
-    list(REMOVE_ITEM DCMTK_SOURCES 
-      ${DCMTK_SOURCES_DIR}/oflog/libsrc/windebap.cc
-      ${DCMTK_SOURCES_DIR}/oflog/libsrc/winsock.cc
-      )
-  elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
-    list(REMOVE_ITEM DCMTK_SOURCES 
-      ${DCMTK_SOURCES_DIR}/oflog/libsrc/unixsock.cc
-      )
-
-    if (${CMAKE_COMPILER_IS_GNUCXX})
-      # This is a patch for MinGW64
-      execute_process(
-        COMMAND patch -p0 -i ${CMAKE_SOURCE_DIR}/Resources/Patches/dcmtk-mingw64.patch
-        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
-        )
-    endif()
-
-  endif()
-
-  list(REMOVE_ITEM DCMTK_SOURCES 
-    ${DCMTK_SOURCES_DIR}/dcmdata/libsrc/mkdictbi.cc
-    ${DCMTK_SOURCES_DIR}/dcmdata/libsrc/mkdeftag.cc
-    ${DCMTK_SOURCES_DIR}/dcmdata/libsrc/dcdictbi.cc
-    )
-
-  # This fixes crashes related to the destruction of the DCMTK OFLogger
-  # http://support.dcmtk.org/docs-snapshot/file_macros.html
-  add_definitions(
-    -DLOG4CPLUS_DISABLE_FATAL=1
-    -DDCMTK_VERSION_NUMBER=360
-    )
-
-  include_directories(
-    #${DCMTK_SOURCES_DIR}
-    ${DCMTK_SOURCES_DIR}/config/include
-    ${DCMTK_SOURCES_DIR}/dcmnet/include
-    ${DCMTK_SOURCES_DIR}/ofstd/include
-    ${DCMTK_SOURCES_DIR}/oflog/include
-    ${DCMTK_SOURCES_DIR}/dcmdata/include
-    )
-
-  source_group(ThirdParty\\Dcmtk REGULAR_EXPRESSION ${DCMTK_SOURCES_DIR}/.*)
-
-  set(DCMTK_BUNDLES_LOG4CPLUS 1)
-
-  if (STANDALONE_BUILD)
-    add_definitions(-DDCMTK_USE_EMBEDDED_DICTIONARIES=1)
-  else()
-    add_definitions(-DDCMTK_USE_EMBEDDED_DICTIONARIES=0)
-  endif()
-
-  set(DCMTK_DICTIONARIES
-    DICTIONARY_DICOM ${DCMTK_SOURCES_DIR}/dcmdata/data/dicom.dic
-    DICTIONARY_PRIVATE ${DCMTK_SOURCES_DIR}/dcmdata/data/private.dic
-    DICTIONARY_DICONDE ${DCMTK_SOURCES_DIR}/dcmdata/data/diconde.dic
-    )
-
-else()
-  # The following line allows to manually add libraries at the
-  # command-line, which is necessary for Ubuntu/Debian packages
-  set(tmp "${DCMTK_LIBRARIES}")
-  include(FindDCMTK)
-  list(APPEND DCMTK_LIBRARIES "${tmp}")
-
-  include_directories(${DCMTK_INCLUDE_DIR})
-  link_libraries(${DCMTK_LIBRARIES})
-
-  add_definitions(
-    -DHAVE_CONFIG_H=1
-    )
-
-  if (EXISTS "${DCMTK_DIR}/config/cfunix.h")
-    set(DCMTK_CONFIGURATION_FILE "${DCMTK_DIR}/config/cfunix.h")
-  elseif (EXISTS "${DCMTK_DIR}/config/osconfig.h")  # This is for Arch Linux
-    set(DCMTK_CONFIGURATION_FILE "${DCMTK_DIR}/config/osconfig.h")
-  else()
-    message(FATAL_ERROR "Please install libdcmtk1-dev")
-  endif()
-
-  # Autodetection of the version of DCMTK
-  file(STRINGS
-    "${DCMTK_CONFIGURATION_FILE}" 
-    DCMTK_VERSION_NUMBER1 REGEX
-    ".*PACKAGE_VERSION .*")    
-
-  string(REGEX REPLACE
-    ".*PACKAGE_VERSION.*\"([0-9]*)\\.([0-9]*)\\.([0-9]*)\"$"
-    "\\1\\2\\3" 
-    DCMTK_VERSION_NUMBER 
-    ${DCMTK_VERSION_NUMBER1})
-
-  add_definitions(-DDCMTK_USE_EMBEDDED_DICTIONARIES=0)
-
-endif()
-
-add_definitions(-DDCMTK_VERSION_NUMBER=${DCMTK_VERSION_NUMBER})
-message("DCMTK version: ${DCMTK_VERSION_NUMBER}")
+# Lookup for DICOM dictionaries, if none is specified by the user
+if (DCMTK_DICTIONARY_DIR STREQUAL "")
+  find_path(DCMTK_DICTIONARY_DIR_AUTO dicom.dic
+    /usr/share/dcmtk
+    /usr/share/libdcmtk2
+    )
+
+  message("Autodetected path to the DICOM dictionaries: ${DCMTK_DICTIONARY_DIR_AUTO}")
+  add_definitions(-DDCMTK_DICTIONARY_DIR="${DCMTK_DICTIONARY_DIR_AUTO}")
+else()
+  add_definitions(-DDCMTK_DICTIONARY_DIR="${DCMTK_DICTIONARY_DIR}")
+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)
+  DownloadPackage(
+    "219ad631b82031806147e4abbfba4fa4"
+    "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/dcmtk-3.6.0.zip" 
+    "${DCMTK_SOURCES_DIR}")
+
+  IF(CMAKE_CROSSCOMPILING)
+    SET(C_CHAR_UNSIGNED 1 CACHE INTERNAL "Whether char is unsigned.")
+  ENDIF()
+  SET(DCMTK_SOURCE_DIR ${CMAKE_BINARY_DIR}/dcmtk-3.6.0)
+  include(${DCMTK_SOURCES_DIR}/CMake/CheckFunctionWithHeaderExists.cmake)
+  include(${DCMTK_SOURCES_DIR}/CMake/GenerateDCMTKConfigure.cmake)
+
+  if ("${CMAKE_SYSTEM_VERSION}" STREQUAL "LinuxStandardBase")
+    set(HAVE_SSTREAM 1)
+    set(HAVE_PROTOTYPE_BZERO 1)
+    set(HAVE_PROTOTYPE_GETHOSTNAME 1)
+    set(HAVE_PROTOTYPE_GETSOCKOPT 1)
+    set(HAVE_PROTOTYPE_SETSOCKOPT 1)
+    set(HAVE_PROTOTYPE_CONNECT 1)
+    set(HAVE_PROTOTYPE_BIND 1)
+    set(HAVE_PROTOTYPE_ACCEPT 1)
+    set(HAVE_PROTOTYPE_SETSOCKNAME 1)
+    set(HAVE_PROTOTYPE_GETSOCKNAME 1)
+  endif()
+
+  CONFIGURE_FILE(
+    ${DCMTK_SOURCES_DIR}/CMake/osconfig.h.in
+    ${DCMTK_SOURCES_DIR}/config/include/dcmtk/config/osconfig.h)
+
+  AUX_SOURCE_DIRECTORY(${DCMTK_SOURCES_DIR}/dcmnet/libsrc DCMTK_SOURCES)
+  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")
+    list(REMOVE_ITEM DCMTK_SOURCES 
+      ${DCMTK_SOURCES_DIR}/oflog/libsrc/windebap.cc
+      ${DCMTK_SOURCES_DIR}/oflog/libsrc/winsock.cc
+      )
+  elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
+    list(REMOVE_ITEM DCMTK_SOURCES 
+      ${DCMTK_SOURCES_DIR}/oflog/libsrc/unixsock.cc
+      )
+
+    if (${CMAKE_COMPILER_IS_GNUCXX})
+      # This is a patch for MinGW64
+      execute_process(
+        COMMAND patch -p0 -i ${CMAKE_SOURCE_DIR}/Resources/Patches/dcmtk-mingw64.patch
+        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+        )
+    endif()
+
+  endif()
+
+  list(REMOVE_ITEM DCMTK_SOURCES 
+    ${DCMTK_SOURCES_DIR}/dcmdata/libsrc/mkdictbi.cc
+    ${DCMTK_SOURCES_DIR}/dcmdata/libsrc/mkdeftag.cc
+    ${DCMTK_SOURCES_DIR}/dcmdata/libsrc/dcdictbi.cc
+    )
+
+  # This fixes crashes related to the destruction of the DCMTK OFLogger
+  # http://support.dcmtk.org/docs-snapshot/file_macros.html
+  add_definitions(
+    -DLOG4CPLUS_DISABLE_FATAL=1
+    -DDCMTK_VERSION_NUMBER=360
+    )
+
+  include_directories(
+    #${DCMTK_SOURCES_DIR}
+    ${DCMTK_SOURCES_DIR}/config/include
+    ${DCMTK_SOURCES_DIR}/dcmnet/include
+    ${DCMTK_SOURCES_DIR}/ofstd/include
+    ${DCMTK_SOURCES_DIR}/oflog/include
+    ${DCMTK_SOURCES_DIR}/dcmdata/include
+    )
+
+  source_group(ThirdParty\\Dcmtk REGULAR_EXPRESSION ${DCMTK_SOURCES_DIR}/.*)
+
+  set(DCMTK_BUNDLES_LOG4CPLUS 1)
+
+  if (STANDALONE_BUILD)
+    add_definitions(-DDCMTK_USE_EMBEDDED_DICTIONARIES=1)
+  else()
+    add_definitions(-DDCMTK_USE_EMBEDDED_DICTIONARIES=0)
+  endif()
+
+  set(DCMTK_DICTIONARIES
+    DICTIONARY_DICOM ${DCMTK_SOURCES_DIR}/dcmdata/data/dicom.dic
+    DICTIONARY_PRIVATE ${DCMTK_SOURCES_DIR}/dcmdata/data/private.dic
+    DICTIONARY_DICONDE ${DCMTK_SOURCES_DIR}/dcmdata/data/diconde.dic
+    )
+
+else()
+  # The following line allows to manually add libraries at the
+  # command-line, which is necessary for Ubuntu/Debian packages
+  set(tmp "${DCMTK_LIBRARIES}")
+  include(FindDCMTK)
+  list(APPEND DCMTK_LIBRARIES "${tmp}")
+
+  include_directories(${DCMTK_INCLUDE_DIR})
+  link_libraries(${DCMTK_LIBRARIES})
+
+  add_definitions(
+    -DHAVE_CONFIG_H=1
+    )
+
+  if (EXISTS "${DCMTK_DIR}/config/cfunix.h")
+    set(DCMTK_CONFIGURATION_FILE "${DCMTK_DIR}/config/cfunix.h")
+  elseif (EXISTS "${DCMTK_DIR}/config/osconfig.h")  # This is for Arch Linux
+    set(DCMTK_CONFIGURATION_FILE "${DCMTK_DIR}/config/osconfig.h")
+  else()
+    message(FATAL_ERROR "Please install libdcmtk1-dev")
+  endif()
+
+  # Autodetection of the version of DCMTK
+  file(STRINGS
+    "${DCMTK_CONFIGURATION_FILE}" 
+    DCMTK_VERSION_NUMBER1 REGEX
+    ".*PACKAGE_VERSION .*")    
+
+  string(REGEX REPLACE
+    ".*PACKAGE_VERSION.*\"([0-9]*)\\.([0-9]*)\\.([0-9]*)\"$"
+    "\\1\\2\\3" 
+    DCMTK_VERSION_NUMBER 
+    ${DCMTK_VERSION_NUMBER1})
+
+  add_definitions(-DDCMTK_USE_EMBEDDED_DICTIONARIES=0)
+
+endif()
+
+add_definitions(-DDCMTK_VERSION_NUMBER=${DCMTK_VERSION_NUMBER})
+message("DCMTK version: ${DCMTK_VERSION_NUMBER}")
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/UnitTestsSources/ImageProcessingTests.cpp	Tue Jun 10 17:28:04 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	Tue Jun 10 17:28:04 2014 +0200
@@ -0,0 +1,112 @@
+/**
+ * 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;
+
+
+TEST(JpegLossless, Basic)
+{
+#if 0
+  // Fallback
+
+  std::string s;
+  Toolbox::ReadFile(s, "IM-0001-1001-0001.dcm");
+
+  ParsedDicomFile parsed(s);
+  DcmFileFormat& dicom = *reinterpret_cast<DcmFileFormat*>(parsed.GetDcmtkObject());
+
+  DcmDataset* dataset = dicom.getDataset();
+
+  dataset->chooseRepresentation(EXS_LittleEndianExplicit, NULL);
+
+  if (dataset->canWriteXfer(EXS_LittleEndianExplicit))
+  {
+    printf("ICI\n");
+
+    parsed.SaveToFile("tutu.dcm");
+
+    // decompress data set if compressed
+    dataset->chooseRepresentation(EXS_LittleEndianExplicit, NULL);
+
+    DcmXfer original_xfer(dataset->getOriginalXfer());
+    std::cout << original_xfer.getXferName() << std::endl;
+
+    FromDcmtkBridge::ExtractPngImage(s, *dataset, 1, ImageExtractionMode_Preview);
+    //fileformat.saveFile("test_decompressed.dcm", EXS_LittleEndianExplicit);
+  }
+#else
+  DcmFileFormat fileformat;
+  //ASSERT_TRUE(fileformat.loadFile("IM-0001-1001-0001.dcm").good());
+  //ASSERT_TRUE(fileformat.loadFile("tata.dcm").good());
+  ASSERT_TRUE(fileformat.loadFile("RG2_JPLY").good());
+  
+  DcmDataset& dataset = *fileformat.getDataset();
+
+  //ASSERT_TRUE(DicomImageDecoder::IsJpegLossless(dataset));
+
+  ImageBuffer image;
+  //DicomImageDecoder::DecodeJpegLossless(image, dataset, 0);
+  DicomImageDecoder::Decode(image, dataset, 0);
+
+  ImageAccessor accessor(image.GetAccessor());
+
+  for (unsigned int y = 0; y < accessor.GetHeight(); y++)
+  {
+    int16_t *p = reinterpret_cast<int16_t*>(accessor.GetRow(y));
+    for (unsigned int x = 0; x < accessor.GetWidth(); x++, p ++)
+    {
+      if (*p < 0)
+        *p = 0;
+    }
+  }
+
+  PngWriter w;
+  w.WriteToFile("tata.png", accessor);
+#endif
+}
+
+
+#endif