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 
init()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:
png_reader(FILE * file)170     png_reader(FILE* file          ) : file_mgr(file)           { init(); }
png_reader(const char * filename)171     png_reader(const char* filename) : file_mgr(filename, "rb") { init(); }
172 
~png_reader()173     ~png_reader() {
174         png_destroy_read_struct(&_png_ptr,&_info_ptr,NULL);
175     }
get_dimensions()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>
apply(const View & 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>
read_image(Image & im)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:
png_reader_color_convert(FILE * file,CC cc_in)218     png_reader_color_convert(FILE* file          ,CC cc_in) : png_reader(file),_cc(cc_in) {}
png_reader_color_convert(FILE * file)219     png_reader_color_convert(FILE* file          ) : png_reader(file) {}
png_reader_color_convert(const char * filename,CC cc_in)220     png_reader_color_convert(const char* filename,CC cc_in) : png_reader(filename),_cc(cc_in) {}
png_reader_color_convert(const char * filename)221     png_reader_color_convert(const char* filename) : png_reader(filename) {}
222     template <typename View>
apply(const View & 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>
read_image(Image & im)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 
init()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:
png_writer(FILE * file)327     png_writer(FILE* file          ) : file_mgr(file)           { init(); }
png_writer(const char * filename)328     png_writer(const char* filename) : file_mgr(filename, "wb") { init(); }
329 
~png_writer()330     ~png_writer() {
331         png_destroy_write_struct(&_png_ptr,&_info_ptr);
332     }
333     template <typename View>
apply(const View & 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
361