Mercurial > hg > orthanc
changeset 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 | b71c59312bae |
children | 0511feaf0ec2 |
files | Core/Images/PamReader.cpp Core/Images/PamReader.h Core/Images/PamWriter.cpp Core/Images/PamWriter.h |
diffstat | 4 files changed, 207 insertions(+), 195 deletions(-) [+] |
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(); } - }
--- a/Core/Images/PamReader.h Fri Jul 06 15:18:53 2018 +0200 +++ b/Core/Images/PamReader.h Fri Jul 06 16:33:03 2018 +0200 @@ -35,18 +35,12 @@ #include "ImageAccessor.h" -#include "../Enumerations.h" - -#include <vector> -#include <stdint.h> -#include <boost/shared_ptr.hpp> -#include <boost/noncopyable.hpp> -#include <istream> - #if !defined(ORTHANC_SANDBOXED) # error The macro ORTHANC_SANDBOXED must be defined #endif +#include <boost/noncopyable.hpp> + namespace Orthanc { class PamReader : @@ -54,22 +48,15 @@ public boost::noncopyable { private: - std::vector<char> data_; + void ParseContent(); + + std::string content_; public: - PamReader() {} - virtual ~PamReader() {} - #if ORTHANC_SANDBOXED == 0 void ReadFromFile(const std::string& filename); #endif - void ReadFromMemory(const void* buffer, - size_t size); - void ReadFromMemory(const std::string& buffer); - - protected: - void ReadFromStream(std::istream& input); }; }
--- a/Core/Images/PamWriter.cpp Fri Jul 06 15:18:53 2018 +0200 +++ b/Core/Images/PamWriter.cpp Fri Jul 06 16:33:03 2018 +0200 @@ -34,136 +34,120 @@ #include "../PrecompiledHeaders.h" #include "PamWriter.h" -#include "../ChunkedBuffer.h" #include "../Endianness.h" #include "../OrthancException.h" #include "../Toolbox.h" -#include <vector> -#include <stdint.h> -#include <iostream> -#include <sstream> -#include <fstream> - -#if ORTHANC_SANDBOXED == 0 -# include "../SystemToolbox.h" -#endif +#include <boost/lexical_cast.hpp> namespace Orthanc { - namespace + static void GetPixelFormatInfo(const PixelFormat& format, + unsigned int& maxValue, + unsigned int& channelCount, + unsigned int& bytesPerChannel, + std::string& tupleType) { - void GetPixelFormatInfo(const PixelFormat& format, unsigned int& maxValue, unsigned int& channelCount, unsigned int& bytesPerChannel, const char*& tupleType) { - maxValue = 255; - channelCount = 1; - bytesPerChannel = 1; - tupleType = NULL; - - switch (format) { + switch (format) + { case PixelFormat_Grayscale8: maxValue = 255; channelCount = 1; bytesPerChannel = 1; tupleType = "GRAYSCALE"; break; + case PixelFormat_Grayscale16: maxValue = 65535; channelCount = 1; bytesPerChannel = 2; tupleType = "GRAYSCALE"; break; + case PixelFormat_RGB24: maxValue = 255; channelCount = 3; bytesPerChannel = 1; tupleType = "RGB"; break; + case PixelFormat_RGB48: maxValue = 255; channelCount = 3; bytesPerChannel = 2; tupleType = "RGB"; break; + default: throw OrthancException(ErrorCode_NotImplemented); - } - } - - void WriteToStream(std::ostream& output, - unsigned int width, - unsigned int height, - unsigned int pitch, - PixelFormat format, - const void* buffer) - { - unsigned int maxValue = 255; - unsigned int channelCount = 1; - unsigned int bytesPerChannel = 1; - const char* tupleType = "GRAYSCALE"; - GetPixelFormatInfo(format, maxValue, channelCount, bytesPerChannel, tupleType); - - output << "P7" << "\n"; - output << "WIDTH " << width << "\n"; - output << "HEIGHT " << height << "\n"; - output << "DEPTH " << channelCount << "\n"; - output << "MAXVAL " << maxValue << "\n"; - output << "TUPLTYPE " << tupleType << "\n"; - output << "ENDHDR" << "\n"; - - if (Toolbox::DetectEndianness() == Endianness_Little && bytesPerChannel == 2) - { - uint16_t tmp; - const uint16_t* pixel = NULL; - for (unsigned int h = 0; h < height; ++h) - { - pixel = reinterpret_cast<const uint16_t*> (reinterpret_cast<const uint8_t*>(buffer) + h * pitch); - for (unsigned int w = 0; w < (width * channelCount); ++w, ++pixel) - { - tmp = htobe16(*pixel); - output.write(reinterpret_cast<const char*>(&tmp), 2); - } - } - } - else - { - for (unsigned int h = 0; h < height; ++h) - { - output.write(reinterpret_cast<const char*>(reinterpret_cast<const uint8_t*>(buffer) + h * pitch), channelCount * bytesPerChannel * width); - } - } - - } - } -#if ORTHANC_SANDBOXED == 0 - void PamWriter::WriteToFileInternal(const std::string& filename, - unsigned int width, - unsigned int height, - unsigned int pitch, - PixelFormat format, - const void* buffer) - { - std::ofstream outfile (filename, std::ofstream::binary); - - WriteToStream(outfile, width, height, pitch, format, buffer); - outfile.close(); - } -#endif - - - void PamWriter::WriteToMemoryInternal(std::string& output, + + void PamWriter::WriteToMemoryInternal(std::string& target, unsigned int width, unsigned int height, - unsigned int pitch, + unsigned int sourcePitch, PixelFormat format, const void* buffer) { - std::ostringstream outStream; // todo: try to write directly in output and avoid copy + unsigned int maxValue, channelCount, bytesPerChannel; + std::string tupleType; + GetPixelFormatInfo(format, maxValue, channelCount, bytesPerChannel, tupleType); + + target = (std::string("P7") + + std::string("\nWIDTH ") + boost::lexical_cast<std::string>(width) + + std::string("\nHEIGHT ") + boost::lexical_cast<std::string>(height) + + std::string("\nDEPTH ") + boost::lexical_cast<std::string>(channelCount) + + std::string("\nMAXVAL ") + boost::lexical_cast<std::string>(maxValue) + + std::string("\nTUPLTYPE ") + tupleType + + std::string("\nENDHDR\n")); + + if (bytesPerChannel != 1 && + bytesPerChannel != 2) + { + throw OrthancException(ErrorCode_NotImplemented); + } + + size_t targetPitch = channelCount * bytesPerChannel * width; + size_t offset = target.size(); + + target.resize(offset + targetPitch * height); + + assert(target.size() != 0); - WriteToStream(outStream, width, height, pitch, format, buffer); - output = outStream.str(); + if (Toolbox::DetectEndianness() == Endianness_Little && + bytesPerChannel == 2) + { + // Byte swapping + for (unsigned int h = 0; h < height; ++h) + { + const uint16_t* p = reinterpret_cast<const uint16_t*> + (reinterpret_cast<const uint8_t*>(buffer) + h * sourcePitch); + uint16_t* q = reinterpret_cast<uint16_t*> + (reinterpret_cast<uint8_t*>(&target[offset]) + h * targetPitch); + + for (unsigned int w = 0; w < width * channelCount; ++w) + { + *q = htobe16(*p); + p++; + q++; + } + } + } + else + { + // Either "bytesPerChannel == 1" (and endianness is not + // relevant), or we run on a big endian architecture (and no + // byte swapping is necessary, as PAM uses big endian) + + for (unsigned int h = 0; h < height; ++h) + { + const void* p = reinterpret_cast<const uint8_t*>(buffer) + h * sourcePitch; + void* q = reinterpret_cast<uint8_t*>(&target[offset]) + h * targetPitch; + memcpy(q, p, targetPitch); + } + } } }
--- a/Core/Images/PamWriter.h Fri Jul 06 15:18:53 2018 +0200 +++ b/Core/Images/PamWriter.h Fri Jul 06 16:33:03 2018 +0200 @@ -41,25 +41,11 @@ class PamWriter : public IImageWriter { protected: -#if ORTHANC_SANDBOXED == 0 - virtual void WriteToFileInternal(const std::string& filename, - unsigned int width, - unsigned int height, - unsigned int pitch, - PixelFormat format, - const void* buffer); -#endif - - virtual void WriteToMemoryInternal(std::string& png, + virtual void WriteToMemoryInternal(std::string& target, unsigned int width, unsigned int height, unsigned int pitch, PixelFormat format, const void* buffer); - - public: - PamWriter() {} - - virtual ~PamWriter() {} }; }