]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /* |
2 | Copyright 2005-2007 Adobe Systems Incorporated | |
3 | ||
4 | Use, modification and distribution are subject to the Boost Software License, | |
5 | Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at | |
6 | http://www.boost.org/LICENSE_1_0.txt). | |
7 | ||
8 | See http://stlab.adobe.com/gil for most recent version including documentation. | |
9 | */ | |
10 | /*************************************************************************************************/ | |
11 | ||
12 | #ifndef GIL_PNG_IO_PRIVATE_H | |
13 | #define GIL_PNG_IO_PRIVATE_H | |
14 | ||
15 | /// \file | |
16 | /// \brief Internal support for reading and writing PNG files | |
17 | /// \author Hailin Jin and Lubomir Bourdev \n | |
18 | /// Adobe Systems Incorporated | |
19 | /// \date 2005-2007 \n Last updated August 14, 2007 | |
20 | ||
21 | #include <algorithm> | |
22 | #include <vector> | |
23 | #include <boost/static_assert.hpp> | |
24 | #include "../../gil_all.hpp" | |
25 | #include "io_error.hpp" | |
26 | #include <png.h> | |
27 | ||
28 | namespace boost { namespace gil { | |
29 | ||
30 | namespace detail { | |
31 | ||
32 | static const std::size_t PNG_BYTES_TO_CHECK = 4; | |
33 | ||
34 | // lbourdev: These can be greatly simplified, for example: | |
35 | template <typename Cs> struct png_color_type {BOOST_STATIC_CONSTANT(int,color_type=0);}; | |
36 | template<> struct png_color_type<gray_t> { BOOST_STATIC_CONSTANT(int,color_type=PNG_COLOR_TYPE_GRAY); }; | |
37 | template<> struct png_color_type<rgb_t> { BOOST_STATIC_CONSTANT(int,color_type=PNG_COLOR_TYPE_RGB); }; | |
38 | template<> struct png_color_type<rgba_t> { BOOST_STATIC_CONSTANT(int,color_type=PNG_COLOR_TYPE_RGBA); }; | |
39 | ||
40 | template <typename Channel,typename ColorSpace> struct png_is_supported {BOOST_STATIC_CONSTANT(bool,value=false);}; | |
41 | template <> struct png_is_supported<bits8,gray_t> {BOOST_STATIC_CONSTANT(bool,value=true);}; | |
42 | template <> struct png_is_supported<bits8,rgb_t> {BOOST_STATIC_CONSTANT(bool,value=true);}; | |
43 | template <> struct png_is_supported<bits8,rgba_t> {BOOST_STATIC_CONSTANT(bool,value=true);}; | |
44 | template <> struct png_is_supported<bits16,gray_t> {BOOST_STATIC_CONSTANT(bool,value=true);}; | |
45 | template <> struct png_is_supported<bits16,rgb_t> {BOOST_STATIC_CONSTANT(bool,value=true);}; | |
46 | template <> struct png_is_supported<bits16,rgba_t> {BOOST_STATIC_CONSTANT(bool,value=true);}; | |
47 | ||
48 | template <typename Channel> struct png_bit_depth {BOOST_STATIC_CONSTANT(int,bit_depth=sizeof(Channel)*8);}; | |
49 | ||
50 | template <typename Channel,typename ColorSpace> | |
51 | struct png_read_support_private { | |
52 | BOOST_STATIC_CONSTANT(bool,is_supported=false); | |
53 | BOOST_STATIC_CONSTANT(int,bit_depth=0); | |
54 | BOOST_STATIC_CONSTANT(int,color_type=0); | |
55 | }; | |
56 | template <> | |
57 | struct png_read_support_private<bits8,gray_t> { | |
58 | BOOST_STATIC_CONSTANT(bool,is_supported=true); | |
59 | BOOST_STATIC_CONSTANT(int,bit_depth=8); | |
60 | BOOST_STATIC_CONSTANT(int,color_type=PNG_COLOR_TYPE_GRAY); | |
61 | }; | |
62 | template <> | |
63 | struct png_read_support_private<bits8,rgb_t> { | |
64 | BOOST_STATIC_CONSTANT(bool,is_supported=true); | |
65 | BOOST_STATIC_CONSTANT(int,bit_depth=8); | |
66 | BOOST_STATIC_CONSTANT(int,color_type=PNG_COLOR_TYPE_RGB); | |
67 | }; | |
68 | template <> | |
69 | struct png_read_support_private<bits8,rgba_t> { | |
70 | BOOST_STATIC_CONSTANT(bool,is_supported=true); | |
71 | BOOST_STATIC_CONSTANT(int,bit_depth=8); | |
72 | BOOST_STATIC_CONSTANT(int,color_type=PNG_COLOR_TYPE_RGBA); | |
73 | }; | |
74 | template <> | |
75 | struct png_read_support_private<bits16,gray_t> { | |
76 | BOOST_STATIC_CONSTANT(bool,is_supported=true); | |
77 | BOOST_STATIC_CONSTANT(int,bit_depth=16); | |
78 | BOOST_STATIC_CONSTANT(int,color_type=PNG_COLOR_TYPE_GRAY); | |
79 | }; | |
80 | template <> | |
81 | struct png_read_support_private<bits16,rgb_t> { | |
82 | BOOST_STATIC_CONSTANT(bool,is_supported=true); | |
83 | BOOST_STATIC_CONSTANT(int,bit_depth=16); | |
84 | BOOST_STATIC_CONSTANT(int,color_type=PNG_COLOR_TYPE_RGB); | |
85 | }; | |
86 | template <> | |
87 | struct png_read_support_private<bits16,rgba_t> { | |
88 | BOOST_STATIC_CONSTANT(bool,is_supported=true); | |
89 | BOOST_STATIC_CONSTANT(int,bit_depth=16); | |
90 | BOOST_STATIC_CONSTANT(int,color_type=PNG_COLOR_TYPE_RGBA); | |
91 | }; | |
92 | ||
93 | template <typename Channel,typename ColorSpace> | |
94 | struct png_write_support_private { | |
95 | BOOST_STATIC_CONSTANT(bool,is_supported=false); | |
96 | BOOST_STATIC_CONSTANT(int,bit_depth=0); | |
97 | BOOST_STATIC_CONSTANT(int,color_type=0); | |
98 | }; | |
99 | template <> | |
100 | struct png_write_support_private<bits8,gray_t> { | |
101 | BOOST_STATIC_CONSTANT(bool,is_supported=true); | |
102 | BOOST_STATIC_CONSTANT(int,bit_depth=8); | |
103 | BOOST_STATIC_CONSTANT(int,color_type=PNG_COLOR_TYPE_GRAY); | |
104 | }; | |
105 | template <> | |
106 | struct png_write_support_private<bits8,rgb_t> { | |
107 | BOOST_STATIC_CONSTANT(bool,is_supported=true); | |
108 | BOOST_STATIC_CONSTANT(int,bit_depth=8); | |
109 | BOOST_STATIC_CONSTANT(int,color_type=PNG_COLOR_TYPE_RGB); | |
110 | }; | |
111 | template <> | |
112 | struct png_write_support_private<bits8,rgba_t> { | |
113 | BOOST_STATIC_CONSTANT(bool,is_supported=true); | |
114 | BOOST_STATIC_CONSTANT(int,bit_depth=8); | |
115 | BOOST_STATIC_CONSTANT(int,color_type=PNG_COLOR_TYPE_RGBA); | |
116 | }; | |
117 | template <> | |
118 | struct png_write_support_private<bits16,gray_t> { | |
119 | BOOST_STATIC_CONSTANT(bool,is_supported=true); | |
120 | BOOST_STATIC_CONSTANT(int,bit_depth=16); | |
121 | BOOST_STATIC_CONSTANT(int,color_type=PNG_COLOR_TYPE_GRAY); | |
122 | }; | |
123 | template <> | |
124 | struct png_write_support_private<bits16,rgb_t> { | |
125 | BOOST_STATIC_CONSTANT(bool,is_supported=true); | |
126 | BOOST_STATIC_CONSTANT(int,bit_depth=16); | |
127 | BOOST_STATIC_CONSTANT(int,color_type=PNG_COLOR_TYPE_RGB); | |
128 | }; | |
129 | template <> | |
130 | struct png_write_support_private<bits16,rgba_t> { | |
131 | BOOST_STATIC_CONSTANT(bool,is_supported=true); | |
132 | BOOST_STATIC_CONSTANT(int,bit_depth=16); | |
133 | BOOST_STATIC_CONSTANT(int,color_type=PNG_COLOR_TYPE_RGBA); | |
134 | }; | |
135 | ||
136 | class png_reader : public file_mgr { | |
137 | protected: | |
138 | png_structp _png_ptr; | |
139 | png_infop _info_ptr; | |
140 | ||
141 | void init() { | |
142 | char buf[PNG_BYTES_TO_CHECK]; | |
143 | // read in some of the signature bytes | |
144 | io_error_if(fread(buf, 1, PNG_BYTES_TO_CHECK, get()) != detail::PNG_BYTES_TO_CHECK, | |
145 | "png_check_validity: fail to read file"); | |
146 | // compare the first PNG_BYTES_TO_CHECK bytes of the signature. | |
147 | io_error_if(png_sig_cmp((png_bytep)buf, (png_size_t)0, detail::PNG_BYTES_TO_CHECK)!=0, | |
148 | "png_check_validity: invalid png file"); | |
149 | ||
150 | _png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL); | |
151 | io_error_if(_png_ptr==NULL,"png_get_file_size: fail to call png_create_write_struct()"); | |
152 | // allocate/initialize the image information data | |
153 | _info_ptr = png_create_info_struct(_png_ptr); | |
154 | if (_info_ptr == NULL) { | |
155 | png_destroy_read_struct(&_png_ptr,NULL,NULL); | |
156 | io_error("png_get_file_size: fail to call png_create_info_struct()"); | |
157 | } | |
158 | if (setjmp(png_jmpbuf(_png_ptr))) { | |
159 | //free all of the memory associated with the png_ptr and info_ptr | |
160 | png_destroy_read_struct(&_png_ptr, &_info_ptr, NULL); | |
161 | io_error("png_get_file_size: fail to call setjmp()"); | |
162 | } | |
163 | png_init_io(_png_ptr, get()); | |
164 | png_set_sig_bytes(_png_ptr,PNG_BYTES_TO_CHECK); | |
165 | png_read_info(_png_ptr, _info_ptr); | |
166 | if (little_endian() && png_get_bit_depth(_png_ptr,_info_ptr)>8) | |
167 | png_set_swap(_png_ptr); | |
168 | } | |
169 | public: | |
170 | png_reader(FILE* file ) : file_mgr(file) { init(); } | |
171 | png_reader(const char* filename) : file_mgr(filename, "rb") { init(); } | |
172 | ||
173 | ~png_reader() { | |
174 | png_destroy_read_struct(&_png_ptr,&_info_ptr,NULL); | |
175 | } | |
176 | point2<std::ptrdiff_t> get_dimensions() { | |
177 | return point2<std::ptrdiff_t>(png_get_image_width(_png_ptr,_info_ptr), | |
178 | png_get_image_height(_png_ptr,_info_ptr)); | |
179 | } | |
180 | template <typename View> | |
181 | void apply(const View& view) { | |
182 | png_uint_32 width, height; | |
183 | int bit_depth, color_type, interlace_type; | |
184 | png_get_IHDR(_png_ptr, _info_ptr, | |
185 | &width, &height,&bit_depth,&color_type,&interlace_type, | |
186 | NULL, NULL); | |
187 | io_error_if(((png_uint_32)view.width()!=width || (png_uint_32)view.height()!= height), | |
188 | "png_read_view: input view size does not match PNG file size"); | |
189 | ||
190 | if(png_read_support_private<typename channel_type<View>::type, | |
191 | typename color_space_type<View>::type>::bit_depth!=bit_depth || | |
192 | png_read_support_private<typename channel_type<View>::type, | |
193 | typename color_space_type<View>::type>::color_type!=color_type) | |
194 | io_error("png_read_view: input view type is incompatible with the image type"); | |
195 | ||
196 | std::vector<pixel<typename channel_type<View>::type, | |
197 | layout<typename color_space_type<View>::type> > > row(width); | |
198 | for(png_uint_32 y=0;y<height;++y) { | |
199 | png_read_row(_png_ptr,(png_bytep)&row.front(),NULL); | |
200 | std::copy(row.begin(),row.end(),view.row_begin(y)); | |
201 | } | |
202 | png_read_end(_png_ptr,NULL); | |
203 | } | |
204 | ||
205 | template <typename Image> | |
206 | void read_image(Image& im) { | |
207 | im.recreate(get_dimensions()); | |
208 | apply(view(im)); | |
209 | } | |
210 | }; | |
211 | ||
212 | // This code will be simplified... | |
213 | template <typename CC> | |
214 | class png_reader_color_convert : public png_reader { | |
215 | private: | |
216 | CC _cc; | |
217 | public: | |
218 | png_reader_color_convert(FILE* file ,CC cc_in) : png_reader(file),_cc(cc_in) {} | |
219 | png_reader_color_convert(FILE* file ) : png_reader(file) {} | |
220 | png_reader_color_convert(const char* filename,CC cc_in) : png_reader(filename),_cc(cc_in) {} | |
221 | png_reader_color_convert(const char* filename) : png_reader(filename) {} | |
222 | template <typename View> | |
223 | void apply(const View& view) { | |
224 | png_uint_32 width, height; | |
225 | int bit_depth, color_type, interlace_type; | |
226 | png_get_IHDR(_png_ptr, _info_ptr, | |
227 | &width, &height,&bit_depth,&color_type,&interlace_type, | |
228 | int_p_NULL, int_p_NULL); | |
229 | io_error_if(((png_uint_32)view.width()!=width || (png_uint_32)view.height()!= height), | |
230 | "png_reader_color_convert::apply(): input view size does not match PNG file size"); | |
231 | switch (color_type) { | |
232 | case PNG_COLOR_TYPE_GRAY: | |
233 | switch (bit_depth) { | |
234 | case 8: { | |
235 | std::vector<gray8_pixel_t> row(width); | |
236 | for(png_uint_32 y=0;y<height;++y) { | |
237 | png_read_row(_png_ptr,(png_bytep)&row.front(),NULL); | |
238 | std::transform(row.begin(),row.end(),view.row_begin(y),color_convert_deref_fn<gray8_ref_t,typename View::value_type,CC>(_cc)); | |
239 | } | |
240 | break; | |
241 | } | |
242 | case 16: { | |
243 | std::vector<gray16_pixel_t> row(width); | |
244 | for(png_uint_32 y=0;y<height;++y) { | |
245 | png_read_row(_png_ptr,(png_bytep)&row.front(),NULL); | |
246 | std::transform(row.begin(),row.end(),view.row_begin(y),color_convert_deref_fn<gray16_ref_t,typename View::value_type,CC>(_cc)); | |
247 | } | |
248 | break; | |
249 | } | |
250 | default: io_error("png_reader_color_convert::apply(): unknown combination of color type and bit depth"); | |
251 | } | |
252 | break; | |
253 | case PNG_COLOR_TYPE_RGB: | |
254 | switch (bit_depth) { | |
255 | case 8: { | |
256 | std::vector<rgb8_pixel_t> row(width); | |
257 | for(png_uint_32 y=0;y<height;++y) { | |
258 | png_read_row(_png_ptr,(png_bytep)&row.front(),NULL); | |
259 | std::transform(row.begin(),row.end(),view.row_begin(y),color_convert_deref_fn<rgb8_ref_t,typename View::value_type,CC>(_cc)); | |
260 | } | |
261 | break; | |
262 | } | |
263 | case 16: { | |
264 | std::vector<rgb16_pixel_t> row(width); | |
265 | for(png_uint_32 y=0;y<height;++y) { | |
266 | png_read_row(_png_ptr,(png_bytep)&row.front(),NULL); | |
267 | std::transform(row.begin(),row.end(),view.row_begin(y),color_convert_deref_fn<rgb16_ref_t,typename View::value_type,CC>(_cc)); | |
268 | } | |
269 | break; | |
270 | } | |
271 | default: io_error("png_reader_color_convert::apply(): unknown combination of color type and bit depth"); | |
272 | } | |
273 | break; | |
274 | case PNG_COLOR_TYPE_RGBA: | |
275 | switch (bit_depth) { | |
276 | case 8: { | |
277 | std::vector<rgba8_pixel_t> row(width); | |
278 | for(png_uint_32 y=0;y<height;++y) { | |
279 | png_read_row(_png_ptr,(png_bytep)&row.front(),NULL); | |
280 | std::transform(row.begin(),row.end(),view.row_begin(y),color_convert_deref_fn<rgba8_ref_t,typename View::value_type,CC>(_cc)); | |
281 | } | |
282 | break; | |
283 | } | |
284 | case 16: { | |
285 | std::vector<rgba16_pixel_t> row(width); | |
286 | for(png_uint_32 y=0;y<height;++y) { | |
287 | png_read_row(_png_ptr,(png_bytep)&row.front(),NULL); | |
288 | std::transform(row.begin(),row.end(),view.row_begin(y),color_convert_deref_fn<rgba16_ref_t,typename View::value_type,CC>(_cc)); | |
289 | } | |
290 | break; | |
291 | } | |
292 | default: io_error("png_reader_color_convert::apply(): unknown combination of color type and bit depth"); | |
293 | } | |
294 | break; | |
295 | default: io_error("png_reader_color_convert::apply(): unknown color type"); | |
296 | } | |
297 | png_read_end(_png_ptr,NULL); | |
298 | } | |
299 | template <typename Image> | |
300 | void read_image(Image& im) { | |
301 | im.recreate(get_dimensions()); | |
302 | apply(view(im)); | |
303 | } | |
304 | }; | |
305 | ||
306 | ||
307 | class png_writer : public file_mgr { | |
308 | protected: | |
309 | png_structp _png_ptr; | |
310 | png_infop _info_ptr; | |
311 | ||
312 | void init() { | |
313 | _png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL); | |
314 | io_error_if(!_png_ptr,"png_write_initialize: fail to call png_create_write_struct()"); | |
315 | _info_ptr = png_create_info_struct(_png_ptr); | |
316 | if (!_info_ptr) { | |
317 | png_destroy_write_struct(&_png_ptr,NULL); | |
318 | io_error("png_write_initialize: fail to call png_create_info_struct()"); | |
319 | } | |
320 | if (setjmp(png_jmpbuf(_png_ptr))) { | |
321 | png_destroy_write_struct(&_png_ptr, &_info_ptr); | |
322 | io_error("png_write_initialize: fail to call setjmp(png_jmpbuf())"); | |
323 | } | |
324 | png_init_io(_png_ptr,get()); | |
325 | } | |
326 | public: | |
327 | png_writer(FILE* file ) : file_mgr(file) { init(); } | |
328 | png_writer(const char* filename) : file_mgr(filename, "wb") { init(); } | |
329 | ||
330 | ~png_writer() { | |
331 | png_destroy_write_struct(&_png_ptr,&_info_ptr); | |
332 | } | |
333 | template <typename View> | |
334 | void apply(const View& view) { | |
335 | png_set_IHDR(_png_ptr, _info_ptr, view.width(), view.height(), | |
336 | png_write_support_private<typename channel_type<View>::type, | |
337 | typename color_space_type<View>::type>::bit_depth, | |
338 | png_write_support_private<typename channel_type<View>::type, | |
339 | typename color_space_type<View>::type>::color_type, | |
340 | PNG_INTERLACE_NONE, | |
341 | PNG_COMPRESSION_TYPE_DEFAULT,PNG_FILTER_TYPE_DEFAULT); | |
342 | png_write_info(_png_ptr,_info_ptr); | |
343 | if (little_endian() && | |
344 | png_write_support_private<typename channel_type<View>::type, | |
345 | typename color_space_type<View>::type>::bit_depth>8) | |
346 | png_set_swap(_png_ptr); | |
347 | std::vector<pixel<typename channel_type<View>::type, | |
348 | layout<typename color_space_type<View>::type> > > row(view.width()); | |
349 | for(int y=0;y<view.height();++y) { | |
350 | std::copy(view.row_begin(y),view.row_end(y),row.begin()); | |
351 | png_write_row(_png_ptr,(png_bytep)&row.front()); | |
352 | } | |
353 | png_write_end(_png_ptr,_info_ptr); | |
354 | } | |
355 | }; | |
356 | ||
357 | } // namespace detail | |
358 | } } // namespace boost::gil | |
359 | ||
360 | #endif |