comparison OrthancFramework/Sources/Images/PngReader.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/PngReader.cpp@94f4a18a79cc
children bf7b9edf6b81
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 "PngReader.h"
36
37 #include "../OrthancException.h"
38 #include "../Toolbox.h"
39
40 #if ORTHANC_SANDBOXED == 0
41 # include "../SystemToolbox.h"
42 #endif
43
44 #include <png.h>
45 #include <string.h> // For memcpy()
46
47 namespace Orthanc
48 {
49 #if ORTHANC_SANDBOXED == 0
50 namespace
51 {
52 struct FileRabi
53 {
54 FILE* fp_;
55
56 FileRabi(const char* filename)
57 {
58 fp_ = SystemToolbox::OpenFile(filename, FileMode_ReadBinary);
59 if (!fp_)
60 {
61 throw OrthancException(ErrorCode_InexistentFile);
62 }
63 }
64
65 ~FileRabi()
66 {
67 if (fp_)
68 {
69 fclose(fp_);
70 }
71 }
72 };
73 }
74 #endif
75
76
77 struct PngReader::PngRabi
78 {
79 png_structp png_;
80 png_infop info_;
81 png_infop endInfo_;
82
83 void Destruct()
84 {
85 if (png_)
86 {
87 png_destroy_read_struct(&png_, &info_, &endInfo_);
88
89 png_ = NULL;
90 info_ = NULL;
91 endInfo_ = NULL;
92 }
93 }
94
95 PngRabi()
96 {
97 png_ = NULL;
98 info_ = NULL;
99 endInfo_ = NULL;
100
101 png_ = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
102 if (!png_)
103 {
104 throw OrthancException(ErrorCode_NotEnoughMemory);
105 }
106
107 info_ = png_create_info_struct(png_);
108 if (!info_)
109 {
110 png_destroy_read_struct(&png_, NULL, NULL);
111 throw OrthancException(ErrorCode_NotEnoughMemory);
112 }
113
114 endInfo_ = png_create_info_struct(png_);
115 if (!info_)
116 {
117 png_destroy_read_struct(&png_, &info_, NULL);
118 throw OrthancException(ErrorCode_NotEnoughMemory);
119 }
120 }
121
122 ~PngRabi()
123 {
124 Destruct();
125 }
126
127 static void MemoryCallback(png_structp png_ptr,
128 png_bytep data,
129 png_size_t size);
130 };
131
132
133 void PngReader::CheckHeader(const void* header)
134 {
135 int is_png = !png_sig_cmp((png_bytep) header, 0, 8);
136 if (!is_png)
137 {
138 throw OrthancException(ErrorCode_BadFileFormat);
139 }
140 }
141
142 PngReader::PngReader()
143 {
144 }
145
146 void PngReader::Read(PngRabi& rabi)
147 {
148 png_set_sig_bytes(rabi.png_, 8);
149
150 png_read_info(rabi.png_, rabi.info_);
151
152 png_uint_32 width, height;
153 int bit_depth, color_type, interlace_type;
154 int compression_type, filter_method;
155 // get size and bit-depth of the PNG-image
156 png_get_IHDR(rabi.png_, rabi.info_,
157 &width, &height,
158 &bit_depth, &color_type, &interlace_type,
159 &compression_type, &filter_method);
160
161 PixelFormat format;
162 unsigned int pitch;
163
164 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth == 8)
165 {
166 format = PixelFormat_Grayscale8;
167 pitch = width;
168 }
169 else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth == 16)
170 {
171 format = PixelFormat_Grayscale16;
172 pitch = 2 * width;
173
174 if (Toolbox::DetectEndianness() == Endianness_Little)
175 {
176 png_set_swap(rabi.png_);
177 }
178 }
179 else if (color_type == PNG_COLOR_TYPE_RGB && bit_depth == 8)
180 {
181 format = PixelFormat_RGB24;
182 pitch = 3 * width;
183 }
184 else if (color_type == PNG_COLOR_TYPE_RGBA && bit_depth == 8)
185 {
186 format = PixelFormat_RGBA32;
187 pitch = 4 * width;
188 }
189 else
190 {
191 throw OrthancException(ErrorCode_NotImplemented);
192 }
193
194 data_.resize(height * pitch);
195
196 if (height == 0 || width == 0)
197 {
198 // Empty image, we are done
199 AssignEmpty(format);
200 return;
201 }
202
203 png_read_update_info(rabi.png_, rabi.info_);
204
205 std::vector<png_bytep> rows(height);
206 for (size_t i = 0; i < height; i++)
207 {
208 rows[i] = &data_[0] + i * pitch;
209 }
210
211 png_read_image(rabi.png_, &rows[0]);
212
213 AssignWritable(format, width, height, pitch, &data_[0]);
214 }
215
216
217 #if ORTHANC_SANDBOXED == 0
218 void PngReader::ReadFromFile(const std::string& filename)
219 {
220 FileRabi f(filename.c_str());
221
222 char header[8];
223 if (fread(header, 1, 8, f.fp_) != 8)
224 {
225 throw OrthancException(ErrorCode_BadFileFormat);
226 }
227
228 CheckHeader(header);
229
230 PngRabi rabi;
231
232 if (setjmp(png_jmpbuf(rabi.png_)))
233 {
234 rabi.Destruct();
235 throw OrthancException(ErrorCode_BadFileFormat);
236 }
237
238 png_init_io(rabi.png_, f.fp_);
239
240 Read(rabi);
241 }
242 #endif
243
244
245 namespace
246 {
247 struct MemoryBuffer
248 {
249 const uint8_t* buffer_;
250 size_t size_;
251 size_t pos_;
252 bool ok_;
253 };
254 }
255
256
257 void PngReader::PngRabi::MemoryCallback(png_structp png_ptr,
258 png_bytep outBytes,
259 png_size_t byteCountToRead)
260 {
261 MemoryBuffer* from = reinterpret_cast<MemoryBuffer*>(png_get_io_ptr(png_ptr));
262
263 if (!from->ok_)
264 {
265 return;
266 }
267
268 if (from->pos_ + byteCountToRead > from->size_)
269 {
270 from->ok_ = false;
271 return;
272 }
273
274 memcpy(outBytes, from->buffer_ + from->pos_, byteCountToRead);
275
276 from->pos_ += byteCountToRead;
277 }
278
279
280 void PngReader::ReadFromMemory(const void* buffer,
281 size_t size)
282 {
283 if (size < 8)
284 {
285 throw OrthancException(ErrorCode_BadFileFormat);
286 }
287
288 CheckHeader(buffer);
289
290 PngRabi rabi;
291
292 if (setjmp(png_jmpbuf(rabi.png_)))
293 {
294 rabi.Destruct();
295 throw OrthancException(ErrorCode_BadFileFormat);
296 }
297
298 MemoryBuffer tmp;
299 tmp.buffer_ = reinterpret_cast<const uint8_t*>(buffer) + 8; // We skip the header
300 tmp.size_ = size - 8;
301 tmp.pos_ = 0;
302 tmp.ok_ = true;
303
304 png_set_read_fn(rabi.png_, &tmp, PngRabi::MemoryCallback);
305
306 Read(rabi);
307
308 if (!tmp.ok_)
309 {
310 throw OrthancException(ErrorCode_BadFileFormat);
311 }
312 }
313
314 void PngReader::ReadFromMemory(const std::string& buffer)
315 {
316 if (buffer.size() != 0)
317 {
318 ReadFromMemory(&buffer[0], buffer.size());
319 }
320 else
321 {
322 ReadFromMemory(NULL, 0);
323 }
324 }
325 }