diff Core/Images/PamReader.cpp @ 2705:5c18a22cb981

fix portability of PAM reader/writer
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 06 Jul 2018 16:33:03 +0200
parents f77c7b48f798
children 6356e2ceb493
line wrap: on
line diff
--- a/Core/Images/PamReader.cpp	Fri Jul 06 15:18:53 2018 +0200
+++ b/Core/Images/PamReader.cpp	Fri Jul 06 16:33:03 2018 +0200
@@ -38,149 +38,204 @@
 #include "../OrthancException.h"
 #include "../Toolbox.h"
 
-#include <istream>
-#include <sstream>
-#include <fstream>
-
 #if ORTHANC_SANDBOXED == 0
 #  include "../SystemToolbox.h"
 #endif
 
-#include <string.h>
+#include <boost/algorithm/string/find.hpp>
+#include <boost/lexical_cast.hpp>
+
 
 namespace Orthanc
 {
-  namespace
+  static void GetPixelFormat(PixelFormat& format,
+                             unsigned int& bytesPerChannel,
+                             const unsigned int& maxValue,
+                             const unsigned int& channelCount,
+                             const std::string& tupleType)
   {
-    void GetPixelFormat(PixelFormat& format, unsigned int& bytesPerChannel, const unsigned int& maxValue, const unsigned int& channelCount, const char* tupleType)
+    if (tupleType == "GRAYSCALE" &&
+        channelCount == 1)
     {
-      if (strcmp(tupleType, "GRAYSCALE") == 0 && channelCount == 1)
+      switch (maxValue)
       {
-        if (maxValue == 255)
-        {
+        case 255:
           format = PixelFormat_Grayscale8;
           bytesPerChannel = 1;
           return;
-        }
-        else if (maxValue == 65535)
-        {
+
+        case 65535:
           format = PixelFormat_Grayscale16;
           bytesPerChannel = 2;
           return;
-        }
+
+        default:
+          throw OrthancException(ErrorCode_NotImplemented);
       }
-      else if (strcmp(tupleType, "RGB") == 0 && channelCount == 3)
+    }
+    else if (tupleType == "RGB" &&
+             channelCount == 3)
+    {
+      switch (maxValue)
       {
-        if (maxValue == 255)
-        {
+        case 255:
           format = PixelFormat_RGB24;
           bytesPerChannel = 1;
           return;
-        }
-        else if (maxValue == 65535)
-        {
+
+        case 65535:
           format = PixelFormat_RGB48;
           bytesPerChannel = 2;
           return;
-        }
+
+        default:
+          throw OrthancException(ErrorCode_NotImplemented);
       }
+    }
+    else
+    {
       throw OrthancException(ErrorCode_NotImplemented);
     }
+  }
 
-    void ReadDelimiter(std::istream& input, const char* expectedDelimiter)
+  
+  typedef std::map<std::string, std::string>  Parameters;
+
+  
+  static std::string LookupStringParameter(const Parameters& parameters,
+                                           const std::string& key)
+  {
+    Parameters::const_iterator found = parameters.find(key);
+
+    if (found == parameters.end())
     {
-      std::string delimiter;
-      input >> delimiter;
-      if (delimiter != expectedDelimiter)
-      {
-        throw OrthancException(ErrorCode_BadFileFormat);
-      }
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+    else
+    {
+      return found->second;
     }
+  }
+  
 
-    unsigned int ReadKeyValueUint(std::istream& input, const char* expectedKey)
+  static unsigned int LookupIntegerParameter(const Parameters& parameters,
+                                             const std::string& key)
+  {
+    try
     {
-      std::string key;
-      unsigned int value;
-      input >> key >> value;
-      if (key != expectedKey)
+      int value = boost::lexical_cast<int>(LookupStringParameter(parameters, key));
+
+      if (value < 0)
       {
         throw OrthancException(ErrorCode_BadFileFormat);
       }
-      return value;
+      else
+      {
+        return static_cast<unsigned int>(value);
+      }
+    }
+    catch (boost::bad_lexical_cast&)
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+  }
+  
+
+  void PamReader::ParseContent()
+  {
+    static const std::string headerDelimiter = "ENDHDR\n";
+    
+    boost::iterator_range<std::string::const_iterator> headerRange =
+      boost::algorithm::find_first(content_, headerDelimiter);
+
+    if (!headerRange)
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
     }
 
-    std::string ReadKeyValueString(std::istream& input, const char* expectedKey)
+    std::string header(static_cast<const std::string&>(content_).begin(), headerRange.begin());
+
+    std::vector<std::string> lines;
+    Toolbox::TokenizeString(lines, header, '\n');
+
+    if (lines.size() < 2 ||
+        lines.front() != "P7" ||
+        !lines.back().empty())
     {
-      std::string key;
-      std::string value;
-      input >> key >> value;
-      if (key != expectedKey)
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+
+    Parameters parameters;
+    
+    for (size_t i = 1; i + 1 < lines.size(); i++)
+    {
+      std::vector<std::string> tokens;
+      Toolbox::TokenizeString(tokens, lines[i], ' ');
+
+      if (tokens.size() != 2)
       {
         throw OrthancException(ErrorCode_BadFileFormat);
       }
-      return value;
+      else
+      {
+        parameters[tokens[0]] = tokens[1];
+      }
     }
-  }
 
-  void PamReader::ReadFromStream(std::istream& input)
-  {
-    ReadDelimiter(input, "P7");
-    unsigned int width = ReadKeyValueUint(input, "WIDTH");
-    unsigned int height = ReadKeyValueUint(input, "HEIGHT");
-    unsigned int channelCount = ReadKeyValueUint(input, "DEPTH");
-    unsigned int maxValue = ReadKeyValueUint(input, "MAXVAL");
-    std::string tupleType = ReadKeyValueString(input, "TUPLTYPE");
-    ReadDelimiter(input, "ENDHDR");
-    // skip last EOL
-    char tmp[16];
-    input.getline(tmp, 16);
+    const unsigned int width = LookupIntegerParameter(parameters, "WIDTH");
+    const unsigned int height = LookupIntegerParameter(parameters, "HEIGHT");
+    const unsigned int channelCount = LookupIntegerParameter(parameters, "DEPTH");
+    const unsigned int maxValue = LookupIntegerParameter(parameters, "MAXVAL");
+    const std::string tupleType = LookupStringParameter(parameters, "TUPLTYPE");
 
     unsigned int bytesPerChannel;
     PixelFormat format;
     GetPixelFormat(format, bytesPerChannel, maxValue, channelCount, tupleType.c_str());
 
-    // read the pixels data
-    unsigned int sizeInBytes = width * height * channelCount * bytesPerChannel;
-    data_.reserve(sizeInBytes);
-    input.read(data_.data(), sizeInBytes);
+    unsigned int pitch = width * channelCount * bytesPerChannel;
+
+    if (content_.size() != header.size() + headerDelimiter.size() + pitch * height)
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
 
-    AssignWritable(format, width, height, width * channelCount * bytesPerChannel, data_.data());
+    size_t offset = content_.size() - pitch * height;
+    AssignWritable(format, width, height, pitch, &content_[offset]);
 
-    // swap bytes
+    // Byte swapping if needed
+    if (bytesPerChannel != 1 &&
+        bytesPerChannel != 2)
+    {
+      throw OrthancException(ErrorCode_NotImplemented);
+    }
+    
     if (Toolbox::DetectEndianness() == Endianness_Little && bytesPerChannel == 2)
     {
-      uint16_t* pixel = NULL;
       for (unsigned int h = 0; h < height; ++h)
       {
-        pixel = reinterpret_cast<uint16_t*> (data_.data() + h * width * channelCount * bytesPerChannel);
-        for (unsigned int w = 0; w < (width * channelCount); ++w, ++pixel)
+        uint16_t* pixel = reinterpret_cast<uint16_t*>(GetRow(h));
+        
+        for (unsigned int w = 0; w < GetWidth(); ++w, ++pixel)
         {
           *pixel = htobe16(*pixel);
         }
       }
     }
-
   }
 
+  
 #if ORTHANC_SANDBOXED == 0
   void PamReader::ReadFromFile(const std::string& filename)
   {
-    std::ifstream inputStream(filename, std::ofstream::binary);
-    ReadFromStream(inputStream);
+    SystemToolbox::ReadFile(content_, filename);
+    ParseContent();
   }
 #endif
-
-  void PamReader::ReadFromMemory(const void* buffer,
-                                 size_t size)
-  {
-    std::istringstream inputStream(std::string(reinterpret_cast<const char*>(buffer), size));
-    ReadFromStream(inputStream);
-  }
+  
 
   void PamReader::ReadFromMemory(const std::string& buffer)
   {
-    std::istringstream inputStream(buffer);
-    ReadFromStream(inputStream);
+    content_ = buffer;
+    ParseContent();
   }
-
 }