comparison Resources/Orthanc/Core/Images/PngReader.cpp @ 16:ff1e935768e7

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