comparison Core/Images/PngReader.cpp @ 1612:96582230ddcb

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