comparison OrthancFramework/Sources/Images/PamReader.cpp @ 4044:d25f4c0fa160 framework

splitting code into OrthancFramework and OrthancServer
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 10 Jun 2020 20:30:34 +0200
parents Core/Images/PamReader.cpp@95083d2f6819
children d6b7fb0f9652
comparison
equal deleted inserted replaced
4043:6c6239aec462 4044:d25f4c0fa160
1 /**
2 * Orthanc - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium
5 * Copyright (C) 2017-2020 Osimis S.A., Belgium
6 *
7 * This program is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
11 *
12 * In addition, as a special exception, the copyright holders of this
13 * program give permission to link the code of its release with the
14 * OpenSSL project's "OpenSSL" library (or with modified versions of it
15 * that use the same license as the "OpenSSL" library), and distribute
16 * the linked executables. You must obey the GNU General Public License
17 * in all respects for all of the code used other than "OpenSSL". If you
18 * modify file(s) with this exception, you may extend this exception to
19 * your version of the file(s), but you are not obligated to do so. If
20 * you do not wish to do so, delete this exception statement from your
21 * version. If you delete this exception statement from all source files
22 * in the program, then also delete it here.
23 *
24 * This program is distributed in the hope that it will be useful, but
25 * WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 * General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program. If not, see <http://www.gnu.org/licenses/>.
31 **/
32
33
34 #include "../PrecompiledHeaders.h"
35 #include "PamReader.h"
36
37 #include "../Endianness.h"
38 #include "../OrthancException.h"
39 #include "../Toolbox.h"
40
41 #if ORTHANC_SANDBOXED == 0
42 # include "../SystemToolbox.h"
43 #endif
44
45 #include <stdlib.h> // For malloc/free
46 #include <boost/algorithm/string/find.hpp>
47 #include <boost/lexical_cast.hpp>
48
49
50 namespace Orthanc
51 {
52 static void GetPixelFormat(PixelFormat& format,
53 unsigned int& bytesPerChannel,
54 const unsigned int& maxValue,
55 const unsigned int& channelCount,
56 const std::string& tupleType)
57 {
58 if (tupleType == "GRAYSCALE" &&
59 channelCount == 1)
60 {
61 switch (maxValue)
62 {
63 case 255:
64 format = PixelFormat_Grayscale8;
65 bytesPerChannel = 1;
66 return;
67
68 case 65535:
69 format = PixelFormat_Grayscale16;
70 bytesPerChannel = 2;
71 return;
72
73 default:
74 throw OrthancException(ErrorCode_NotImplemented);
75 }
76 }
77 else if (tupleType == "RGB" &&
78 channelCount == 3)
79 {
80 switch (maxValue)
81 {
82 case 255:
83 format = PixelFormat_RGB24;
84 bytesPerChannel = 1;
85 return;
86
87 case 65535:
88 format = PixelFormat_RGB48;
89 bytesPerChannel = 2;
90 return;
91
92 default:
93 throw OrthancException(ErrorCode_NotImplemented);
94 }
95 }
96 else
97 {
98 throw OrthancException(ErrorCode_NotImplemented);
99 }
100 }
101
102
103 typedef std::map<std::string, std::string> Parameters;
104
105
106 static std::string LookupStringParameter(const Parameters& parameters,
107 const std::string& key)
108 {
109 Parameters::const_iterator found = parameters.find(key);
110
111 if (found == parameters.end())
112 {
113 throw OrthancException(ErrorCode_BadFileFormat);
114 }
115 else
116 {
117 return found->second;
118 }
119 }
120
121
122 static unsigned int LookupIntegerParameter(const Parameters& parameters,
123 const std::string& key)
124 {
125 try
126 {
127 int value = boost::lexical_cast<int>(LookupStringParameter(parameters, key));
128
129 if (value < 0)
130 {
131 throw OrthancException(ErrorCode_BadFileFormat);
132 }
133 else
134 {
135 return static_cast<unsigned int>(value);
136 }
137 }
138 catch (boost::bad_lexical_cast&)
139 {
140 throw OrthancException(ErrorCode_BadFileFormat);
141 }
142 }
143
144
145 void PamReader::ParseContent()
146 {
147 static const std::string headerDelimiter = "ENDHDR\n";
148
149 boost::iterator_range<std::string::const_iterator> headerRange =
150 boost::algorithm::find_first(content_, headerDelimiter);
151
152 if (!headerRange)
153 {
154 throw OrthancException(ErrorCode_BadFileFormat);
155 }
156
157 std::string header(static_cast<const std::string&>(content_).begin(), headerRange.begin());
158
159 std::vector<std::string> lines;
160 Toolbox::TokenizeString(lines, header, '\n');
161
162 if (lines.size() < 2 ||
163 lines.front() != "P7" ||
164 !lines.back().empty())
165 {
166 throw OrthancException(ErrorCode_BadFileFormat);
167 }
168
169 Parameters parameters;
170
171 for (size_t i = 1; i + 1 < lines.size(); i++)
172 {
173 std::vector<std::string> tokens;
174 Toolbox::TokenizeString(tokens, lines[i], ' ');
175
176 if (tokens.size() != 2)
177 {
178 throw OrthancException(ErrorCode_BadFileFormat);
179 }
180 else
181 {
182 parameters[tokens[0]] = tokens[1];
183 }
184 }
185
186 const unsigned int width = LookupIntegerParameter(parameters, "WIDTH");
187 const unsigned int height = LookupIntegerParameter(parameters, "HEIGHT");
188 const unsigned int channelCount = LookupIntegerParameter(parameters, "DEPTH");
189 const unsigned int maxValue = LookupIntegerParameter(parameters, "MAXVAL");
190 const std::string tupleType = LookupStringParameter(parameters, "TUPLTYPE");
191
192 unsigned int bytesPerChannel;
193 PixelFormat format;
194 GetPixelFormat(format, bytesPerChannel, maxValue, channelCount, tupleType.c_str());
195
196 unsigned int pitch = width * channelCount * bytesPerChannel;
197
198 if (content_.size() != header.size() + headerDelimiter.size() + pitch * height)
199 {
200 throw OrthancException(ErrorCode_BadFileFormat);
201 }
202
203 size_t offset = content_.size() - pitch * height;
204
205 {
206 intptr_t bufferAddr = reinterpret_cast<intptr_t>(&content_[offset]);
207 if((bufferAddr % 8) == 0)
208 LOG(TRACE) << "PamReader::ParseContent() image address = " << bufferAddr;
209 else
210 LOG(TRACE) << "PamReader::ParseContent() image address = " << bufferAddr << " (not a multiple of 8!)";
211 }
212
213 // if we want to enforce alignment, we need to use a freshly allocated
214 // buffer, since we have no alignment guarantees on the original one
215 if (enforceAligned_)
216 {
217 if (alignedImageBuffer_ != NULL)
218 free(alignedImageBuffer_);
219 alignedImageBuffer_ = malloc(pitch * height);
220 memcpy(alignedImageBuffer_, &content_[offset], pitch* height);
221 content_ = "";
222 AssignWritable(format, width, height, pitch, alignedImageBuffer_);
223 }
224 else
225 {
226 AssignWritable(format, width, height, pitch, &content_[offset]);
227 }
228
229 // Byte swapping if needed
230 if (bytesPerChannel != 1 &&
231 bytesPerChannel != 2)
232 {
233 throw OrthancException(ErrorCode_NotImplemented);
234 }
235
236 if (Toolbox::DetectEndianness() == Endianness_Little &&
237 bytesPerChannel == 2)
238 {
239 for (unsigned int h = 0; h < height; ++h)
240 {
241 uint16_t* pixel = reinterpret_cast<uint16_t*>(GetRow(h));
242
243 for (unsigned int w = 0; w < GetWidth(); ++w, ++pixel)
244 {
245 #if ORTHANC_ENABLE_WASM == 1
246 /*
247
248 crash (2019-08-05):
249
250 Uncaught abort(alignment fault) at Error
251 at jsStackTrace
252 at stackTrace
253 at abort
254 at alignfault
255 at SAFE_HEAP_LOAD_i32_2_2 (wasm-function[251132]:39)
256 at __ZN7Orthanc9PamReader12ParseContentEv (wasm-function[11457]:8088)
257
258 Web Assembly IS LITTLE ENDIAN!
259
260 Perhaps in htobe16 ?
261 */
262 uint8_t* srcdst = reinterpret_cast<uint8_t*>(pixel);
263 uint8_t tmp = srcdst[0];
264 srcdst[0] = srcdst[1];
265 srcdst[1] = tmp;
266 #else
267 // memcpy() is necessary to avoid segmentation fault if the
268 // "pixel" pointer is not 16-bit aligned (which is the case
269 // if "offset" is an odd number). Check out issue #99:
270 // https://bitbucket.org/sjodogne/orthanc/issues/99
271 uint16_t v = htobe16(*pixel);
272 memcpy(pixel, &v, sizeof(v));
273 #endif
274 }
275 }
276 }
277 }
278
279
280 #if ORTHANC_SANDBOXED == 0
281 void PamReader::ReadFromFile(const std::string& filename)
282 {
283 SystemToolbox::ReadFile(content_, filename);
284 ParseContent();
285 }
286 #endif
287
288
289 void PamReader::ReadFromMemory(const std::string& buffer)
290 {
291 content_ = buffer;
292 ParseContent();
293 }
294
295 void PamReader::ReadFromMemory(const void* buffer,
296 size_t size)
297 {
298 content_.assign(reinterpret_cast<const char*>(buffer), size);
299 ParseContent();
300 }
301
302 PamReader::~PamReader()
303 {
304 if (alignedImageBuffer_ != NULL)
305 {
306 free(alignedImageBuffer_);
307 }
308 }
309 }