comparison Framework/Orthanc/Core/Images/PngReader.cpp @ 1:2dbe613f6c93

add orthanc core
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 14 Oct 2016 15:39:01 +0200
parents
children da2cf3ace87a
comparison
equal deleted inserted replaced
0:351ab0da0150 1:2dbe613f6c93
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 *
6 * This program is free software: you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation, either version 3 of the
9 * License, or (at your option) any later version.
10 *
11 * In addition, as a special exception, the copyright holders of this
12 * program give permission to link the code of its release with the
13 * OpenSSL project's "OpenSSL" library (or with modified versions of it
14 * that use the same license as the "OpenSSL" library), and distribute
15 * the linked executables. You must obey the GNU General Public License
16 * in all respects for all of the code used other than "OpenSSL". If you
17 * modify file(s) with this exception, you may extend this exception to
18 * your version of the file(s), but you are not obligated to do so. If
19 * you do not wish to do so, delete this exception statement from your
20 * version. If you delete this exception statement from all source files
21 * in the program, then also delete it here.
22 *
23 * This program is distributed in the hope that it will be useful, but
24 * WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
26 * General Public License for more details.
27 *
28 * You should have received a copy of the GNU General Public License
29 * along with this program. If not, see <http://www.gnu.org/licenses/>.
30 **/
31
32
33 #include "../PrecompiledHeaders.h"
34 #include "PngReader.h"
35
36 #include "../OrthancException.h"
37 #include "../Toolbox.h"
38
39 #include <png.h>
40 #include <string.h> // For memcpy()
41
42 namespace Orthanc
43 {
44 namespace
45 {
46 struct FileRabi
47 {
48 FILE* fp_;
49
50 FileRabi(const char* filename)
51 {
52 fp_ = Toolbox::OpenFile(filename, FileMode_ReadBinary);
53 if (!fp_)
54 {
55 throw OrthancException(ErrorCode_InexistentFile);
56 }
57 }
58
59 ~FileRabi()
60 {
61 if (fp_)
62 {
63 fclose(fp_);
64 }
65 }
66 };
67 }
68
69
70 struct PngReader::PngRabi
71 {
72 png_structp png_;
73 png_infop info_;
74 png_infop endInfo_;
75
76 void Destruct()
77 {
78 if (png_)
79 {
80 png_destroy_read_struct(&png_, &info_, &endInfo_);
81
82 png_ = NULL;
83 info_ = NULL;
84 endInfo_ = NULL;
85 }
86 }
87
88 PngRabi()
89 {
90 png_ = NULL;
91 info_ = NULL;
92 endInfo_ = NULL;
93
94 png_ = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
95 if (!png_)
96 {
97 throw OrthancException(ErrorCode_NotEnoughMemory);
98 }
99
100 info_ = png_create_info_struct(png_);
101 if (!info_)
102 {
103 png_destroy_read_struct(&png_, NULL, NULL);
104 throw OrthancException(ErrorCode_NotEnoughMemory);
105 }
106
107 endInfo_ = png_create_info_struct(png_);
108 if (!info_)
109 {
110 png_destroy_read_struct(&png_, &info_, NULL);
111 throw OrthancException(ErrorCode_NotEnoughMemory);
112 }
113 }
114
115 ~PngRabi()
116 {
117 Destruct();
118 }
119
120 static void MemoryCallback(png_structp png_ptr,
121 png_bytep data,
122 png_size_t size);
123 };
124
125
126 void PngReader::CheckHeader(const void* header)
127 {
128 int is_png = !png_sig_cmp((png_bytep) header, 0, 8);
129 if (!is_png)
130 {
131 throw OrthancException(ErrorCode_BadFileFormat);
132 }
133 }
134
135 PngReader::PngReader()
136 {
137 }
138
139 void PngReader::Read(PngRabi& rabi)
140 {
141 png_set_sig_bytes(rabi.png_, 8);
142
143 png_read_info(rabi.png_, rabi.info_);
144
145 png_uint_32 width, height;
146 int bit_depth, color_type, interlace_type;
147 int compression_type, filter_method;
148 // get size and bit-depth of the PNG-image
149 png_get_IHDR(rabi.png_, rabi.info_,
150 &width, &height,
151 &bit_depth, &color_type, &interlace_type,
152 &compression_type, &filter_method);
153
154 PixelFormat format;
155 unsigned int pitch;
156
157 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth == 8)
158 {
159 format = PixelFormat_Grayscale8;
160 pitch = width;
161 }
162 else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth == 16)
163 {
164 format = PixelFormat_Grayscale16;
165 pitch = 2 * width;
166
167 if (Toolbox::DetectEndianness() == Endianness_Little)
168 {
169 png_set_swap(rabi.png_);
170 }
171 }
172 else if (color_type == PNG_COLOR_TYPE_RGB && bit_depth == 8)
173 {
174 format = PixelFormat_RGB24;
175 pitch = 3 * width;
176 }
177 else if (color_type == PNG_COLOR_TYPE_RGBA && bit_depth == 8)
178 {
179 format = PixelFormat_RGBA32;
180 pitch = 4 * width;
181 }
182 else
183 {
184 throw OrthancException(ErrorCode_NotImplemented);
185 }
186
187 data_.resize(height * pitch);
188
189 if (height == 0 || width == 0)
190 {
191 // Empty image, we are done
192 AssignEmpty(format);
193 return;
194 }
195
196 png_read_update_info(rabi.png_, rabi.info_);
197
198 std::vector<png_bytep> rows(height);
199 for (size_t i = 0; i < height; i++)
200 {
201 rows[i] = &data_[0] + i * pitch;
202 }
203
204 png_read_image(rabi.png_, &rows[0]);
205
206 AssignWritable(format, width, height, pitch, &data_[0]);
207 }
208
209 void PngReader::ReadFromFile(const std::string& filename)
210 {
211 FileRabi f(filename.c_str());
212
213 char header[8];
214 if (fread(header, 1, 8, f.fp_) != 8)
215 {
216 throw OrthancException(ErrorCode_BadFileFormat);
217 }
218
219 CheckHeader(header);
220
221 PngRabi rabi;
222
223 if (setjmp(png_jmpbuf(rabi.png_)))
224 {
225 rabi.Destruct();
226 throw OrthancException(ErrorCode_BadFileFormat);
227 }
228
229 png_init_io(rabi.png_, f.fp_);
230
231 Read(rabi);
232 }
233
234
235 namespace
236 {
237 struct MemoryBuffer
238 {
239 const uint8_t* buffer_;
240 size_t size_;
241 size_t pos_;
242 bool ok_;
243 };
244 }
245
246
247 void PngReader::PngRabi::MemoryCallback(png_structp png_ptr,
248 png_bytep outBytes,
249 png_size_t byteCountToRead)
250 {
251 MemoryBuffer* from = reinterpret_cast<MemoryBuffer*>(png_get_io_ptr(png_ptr));
252
253 if (!from->ok_)
254 {
255 return;
256 }
257
258 if (from->pos_ + byteCountToRead > from->size_)
259 {
260 from->ok_ = false;
261 return;
262 }
263
264 memcpy(outBytes, from->buffer_ + from->pos_, byteCountToRead);
265
266 from->pos_ += byteCountToRead;
267 }
268
269
270 void PngReader::ReadFromMemory(const void* buffer,
271 size_t size)
272 {
273 if (size < 8)
274 {
275 throw OrthancException(ErrorCode_BadFileFormat);
276 }
277
278 CheckHeader(buffer);
279
280 PngRabi rabi;
281
282 if (setjmp(png_jmpbuf(rabi.png_)))
283 {
284 rabi.Destruct();
285 throw OrthancException(ErrorCode_BadFileFormat);
286 }
287
288 MemoryBuffer tmp;
289 tmp.buffer_ = reinterpret_cast<const uint8_t*>(buffer) + 8; // We skip the header
290 tmp.size_ = size - 8;
291 tmp.pos_ = 0;
292 tmp.ok_ = true;
293
294 png_set_read_fn(rabi.png_, &tmp, PngRabi::MemoryCallback);
295
296 Read(rabi);
297
298 if (!tmp.ok_)
299 {
300 throw OrthancException(ErrorCode_BadFileFormat);
301 }
302 }
303
304 void PngReader::ReadFromMemory(const std::string& buffer)
305 {
306 if (buffer.size() != 0)
307 {
308 ReadFromMemory(&buffer[0], buffer.size());
309 }
310 else
311 {
312 ReadFromMemory(NULL, 0);
313 }
314 }
315 }