Mercurial > hg > orthanc
comparison Core/Images/PamWriter.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 |
comparison
equal
deleted
inserted
replaced
2704:b71c59312bae | 2705:5c18a22cb981 |
---|---|
32 | 32 |
33 | 33 |
34 #include "../PrecompiledHeaders.h" | 34 #include "../PrecompiledHeaders.h" |
35 #include "PamWriter.h" | 35 #include "PamWriter.h" |
36 | 36 |
37 #include "../ChunkedBuffer.h" | |
38 #include "../Endianness.h" | 37 #include "../Endianness.h" |
39 #include "../OrthancException.h" | 38 #include "../OrthancException.h" |
40 #include "../Toolbox.h" | 39 #include "../Toolbox.h" |
41 | 40 |
42 #include <vector> | 41 #include <boost/lexical_cast.hpp> |
43 #include <stdint.h> | |
44 #include <iostream> | |
45 #include <sstream> | |
46 #include <fstream> | |
47 | |
48 #if ORTHANC_SANDBOXED == 0 | |
49 # include "../SystemToolbox.h" | |
50 #endif | |
51 | 42 |
52 | 43 |
53 namespace Orthanc | 44 namespace Orthanc |
54 { | 45 { |
55 namespace | 46 static void GetPixelFormatInfo(const PixelFormat& format, |
47 unsigned int& maxValue, | |
48 unsigned int& channelCount, | |
49 unsigned int& bytesPerChannel, | |
50 std::string& tupleType) | |
56 { | 51 { |
57 void GetPixelFormatInfo(const PixelFormat& format, unsigned int& maxValue, unsigned int& channelCount, unsigned int& bytesPerChannel, const char*& tupleType) { | 52 switch (format) |
58 maxValue = 255; | 53 { |
59 channelCount = 1; | |
60 bytesPerChannel = 1; | |
61 tupleType = NULL; | |
62 | |
63 switch (format) { | |
64 case PixelFormat_Grayscale8: | 54 case PixelFormat_Grayscale8: |
65 maxValue = 255; | 55 maxValue = 255; |
66 channelCount = 1; | 56 channelCount = 1; |
67 bytesPerChannel = 1; | 57 bytesPerChannel = 1; |
68 tupleType = "GRAYSCALE"; | 58 tupleType = "GRAYSCALE"; |
69 break; | 59 break; |
60 | |
70 case PixelFormat_Grayscale16: | 61 case PixelFormat_Grayscale16: |
71 maxValue = 65535; | 62 maxValue = 65535; |
72 channelCount = 1; | 63 channelCount = 1; |
73 bytesPerChannel = 2; | 64 bytesPerChannel = 2; |
74 tupleType = "GRAYSCALE"; | 65 tupleType = "GRAYSCALE"; |
75 break; | 66 break; |
67 | |
76 case PixelFormat_RGB24: | 68 case PixelFormat_RGB24: |
77 maxValue = 255; | 69 maxValue = 255; |
78 channelCount = 3; | 70 channelCount = 3; |
79 bytesPerChannel = 1; | 71 bytesPerChannel = 1; |
80 tupleType = "RGB"; | 72 tupleType = "RGB"; |
81 break; | 73 break; |
74 | |
82 case PixelFormat_RGB48: | 75 case PixelFormat_RGB48: |
83 maxValue = 255; | 76 maxValue = 255; |
84 channelCount = 3; | 77 channelCount = 3; |
85 bytesPerChannel = 2; | 78 bytesPerChannel = 2; |
86 tupleType = "RGB"; | 79 tupleType = "RGB"; |
87 break; | 80 break; |
81 | |
88 default: | 82 default: |
89 throw OrthancException(ErrorCode_NotImplemented); | 83 throw OrthancException(ErrorCode_NotImplemented); |
90 } | |
91 | |
92 } | 84 } |
93 | |
94 void WriteToStream(std::ostream& output, | |
95 unsigned int width, | |
96 unsigned int height, | |
97 unsigned int pitch, | |
98 PixelFormat format, | |
99 const void* buffer) | |
100 { | |
101 unsigned int maxValue = 255; | |
102 unsigned int channelCount = 1; | |
103 unsigned int bytesPerChannel = 1; | |
104 const char* tupleType = "GRAYSCALE"; | |
105 GetPixelFormatInfo(format, maxValue, channelCount, bytesPerChannel, tupleType); | |
106 | |
107 output << "P7" << "\n"; | |
108 output << "WIDTH " << width << "\n"; | |
109 output << "HEIGHT " << height << "\n"; | |
110 output << "DEPTH " << channelCount << "\n"; | |
111 output << "MAXVAL " << maxValue << "\n"; | |
112 output << "TUPLTYPE " << tupleType << "\n"; | |
113 output << "ENDHDR" << "\n"; | |
114 | |
115 if (Toolbox::DetectEndianness() == Endianness_Little && bytesPerChannel == 2) | |
116 { | |
117 uint16_t tmp; | |
118 const uint16_t* pixel = NULL; | |
119 for (unsigned int h = 0; h < height; ++h) | |
120 { | |
121 pixel = reinterpret_cast<const uint16_t*> (reinterpret_cast<const uint8_t*>(buffer) + h * pitch); | |
122 for (unsigned int w = 0; w < (width * channelCount); ++w, ++pixel) | |
123 { | |
124 tmp = htobe16(*pixel); | |
125 output.write(reinterpret_cast<const char*>(&tmp), 2); | |
126 } | |
127 } | |
128 } | |
129 else | |
130 { | |
131 for (unsigned int h = 0; h < height; ++h) | |
132 { | |
133 output.write(reinterpret_cast<const char*>(reinterpret_cast<const uint8_t*>(buffer) + h * pitch), channelCount * bytesPerChannel * width); | |
134 } | |
135 } | |
136 | |
137 } | |
138 | |
139 } | 85 } |
140 | 86 |
141 #if ORTHANC_SANDBOXED == 0 | 87 |
142 void PamWriter::WriteToFileInternal(const std::string& filename, | 88 void PamWriter::WriteToMemoryInternal(std::string& target, |
143 unsigned int width, | |
144 unsigned int height, | |
145 unsigned int pitch, | |
146 PixelFormat format, | |
147 const void* buffer) | |
148 { | |
149 std::ofstream outfile (filename, std::ofstream::binary); | |
150 | |
151 WriteToStream(outfile, width, height, pitch, format, buffer); | |
152 outfile.close(); | |
153 } | |
154 #endif | |
155 | |
156 | |
157 void PamWriter::WriteToMemoryInternal(std::string& output, | |
158 unsigned int width, | 89 unsigned int width, |
159 unsigned int height, | 90 unsigned int height, |
160 unsigned int pitch, | 91 unsigned int sourcePitch, |
161 PixelFormat format, | 92 PixelFormat format, |
162 const void* buffer) | 93 const void* buffer) |
163 { | 94 { |
164 std::ostringstream outStream; // todo: try to write directly in output and avoid copy | 95 unsigned int maxValue, channelCount, bytesPerChannel; |
96 std::string tupleType; | |
97 GetPixelFormatInfo(format, maxValue, channelCount, bytesPerChannel, tupleType); | |
165 | 98 |
166 WriteToStream(outStream, width, height, pitch, format, buffer); | 99 target = (std::string("P7") + |
167 output = outStream.str(); | 100 std::string("\nWIDTH ") + boost::lexical_cast<std::string>(width) + |
101 std::string("\nHEIGHT ") + boost::lexical_cast<std::string>(height) + | |
102 std::string("\nDEPTH ") + boost::lexical_cast<std::string>(channelCount) + | |
103 std::string("\nMAXVAL ") + boost::lexical_cast<std::string>(maxValue) + | |
104 std::string("\nTUPLTYPE ") + tupleType + | |
105 std::string("\nENDHDR\n")); | |
106 | |
107 if (bytesPerChannel != 1 && | |
108 bytesPerChannel != 2) | |
109 { | |
110 throw OrthancException(ErrorCode_NotImplemented); | |
111 } | |
112 | |
113 size_t targetPitch = channelCount * bytesPerChannel * width; | |
114 size_t offset = target.size(); | |
115 | |
116 target.resize(offset + targetPitch * height); | |
117 | |
118 assert(target.size() != 0); | |
119 | |
120 if (Toolbox::DetectEndianness() == Endianness_Little && | |
121 bytesPerChannel == 2) | |
122 { | |
123 // Byte swapping | |
124 for (unsigned int h = 0; h < height; ++h) | |
125 { | |
126 const uint16_t* p = reinterpret_cast<const uint16_t*> | |
127 (reinterpret_cast<const uint8_t*>(buffer) + h * sourcePitch); | |
128 uint16_t* q = reinterpret_cast<uint16_t*> | |
129 (reinterpret_cast<uint8_t*>(&target[offset]) + h * targetPitch); | |
130 | |
131 for (unsigned int w = 0; w < width * channelCount; ++w) | |
132 { | |
133 *q = htobe16(*p); | |
134 p++; | |
135 q++; | |
136 } | |
137 } | |
138 } | |
139 else | |
140 { | |
141 // Either "bytesPerChannel == 1" (and endianness is not | |
142 // relevant), or we run on a big endian architecture (and no | |
143 // byte swapping is necessary, as PAM uses big endian) | |
144 | |
145 for (unsigned int h = 0; h < height; ++h) | |
146 { | |
147 const void* p = reinterpret_cast<const uint8_t*>(buffer) + h * sourcePitch; | |
148 void* q = reinterpret_cast<uint8_t*>(&target[offset]) + h * targetPitch; | |
149 memcpy(q, p, targetPitch); | |
150 } | |
151 } | |
168 } | 152 } |
169 } | 153 } |