1 /*
2  * Copyright (C) 2007,2008   Alex Shulgin
3  *
4  * This file is part of png++ the C++ wrapper for libpng.  PNG++ is free
5  * software; the exact copying conditions are as follows:
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright notice,
11  * this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  * notice, this list of conditions and the following disclaimer in the
15  * documentation and/or other materials provided with the distribution.
16  *
17  * 3. The name of the author may not be used to endorse or promote products
18  * derived from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
23  * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
25  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 #ifndef PNGPP_IMAGE_HPP_INCLUDED
32 #define PNGPP_IMAGE_HPP_INCLUDED
33 
34 #include <fstream>
35 #include "pixel_buffer.hpp"
36 #include "generator.hpp"
37 #include "consumer.hpp"
38 #include "convert_color_space.hpp"
39 
40 namespace png
41 {
42 
43     /**
44      * \brief Class template to represent PNG image.
45      *
46      * The image consists of pixel data as well as additional %image
47      * %info like interlace type, compression method, palette (for
48      * colormap-based images) etc.  Provides methods to read and write
49      * images from/to a generic stream and to manipulate %image pixels.
50      *
51      * The default pixel_buffer stores pixels in a vector of vectors, which
52      * is good for openning, editing or converting an image to any
53      * pixel type.
54      * But for simple and fast image unpacking to one memory chunk this approch
55      * is unacceptable, because it leads to multiple memory allocations, the
56      * unpacked image is spread across the memory and client code needs to
57      * gather it manualy. solid_pixel_buffer solves this problem, but with
58      * restriction: pixels with fractional number of bytes per channel are
59      * not allowed (see solid_pixel_buffer.hpp for details).
60      */
61     template< typename pixel, typename pixel_buffer_type = pixel_buffer< pixel > >
62     class image
63     {
64     public:
65         /**
66          * \brief The pixel traits type for \c pixel.
67          */
68         typedef pixel_traits< pixel > traits;
69 
70         /**
71          * \brief The pixel buffer type for \c pixel.
72          */
73         typedef pixel_buffer_type pixbuf;
74 
75         /**
76          * \brief Represents a row of image pixel data.
77          */
78         typedef typename pixbuf::row_type row_type;
79         typedef typename pixbuf::row_access row_access;
80         typedef typename pixbuf::row_const_access row_const_access;
81 
82         /**
83          * \brief A transformation functor to convert any image to
84          * appropriate color space.
85          */
86         typedef convert_color_space< pixel > transform_convert;
87 
88         /**
89          * \brief The default io transformation: does nothing.
90          */
91         struct transform_identity
92         {
operator ()png::image::transform_identity93             void operator()(io_base&) const {}
94         };
95 
96         /**
97          * \brief Constructs an empty image.
98          */
image()99         image()
100             : m_info(make_image_info< pixel >())
101         {
102         }
103 
104         /**
105          * \brief Constructs an empty image of specified width and height.
106          */
image(uint_32 width,uint_32 height)107         image(uint_32 width, uint_32 height)
108             : m_info(make_image_info< pixel >())
109         {
110             resize(width, height);
111         }
112 
113         /**
114          * \brief Constructs an image reading data from specified file
115          * using default converting transform.
116          */
image(std::string const & filename)117         explicit image(std::string const& filename)
118         {
119             read(filename, transform_convert());
120         }
121 
122         /**
123          * \brief Constructs an image reading data from specified file
124          * using custom transformaton.
125          */
126         template< class transformation >
image(std::string const & filename,transformation const & transform)127         image(std::string const& filename,
128               transformation const& transform)
129         {
130             read(filename.c_str(), transform);
131         }
132 
133         /**
134          * \brief Constructs an image reading data from specified file
135          * using default converting transform.
136          */
image(char const * filename)137         explicit image(char const* filename)
138         {
139             read(filename, transform_convert());
140         }
141 
142         /**
143          * \brief Constructs an image reading data from specified file
144          * using custom transformaton.
145          */
146         template< class transformation >
image(char const * filename,transformation const & transform)147         image(char const* filename, transformation const& transform)
148         {
149             read(filename, transform);
150         }
151 
152         /**
153          * \brief Constructs an image reading data from a stream using
154          * default converting transform.
155          */
image(std::istream & stream)156         explicit image(std::istream& stream)
157         {
158             read_stream(stream, transform_convert());
159         }
160 
161         /**
162          * \brief Constructs an image reading data from a stream using
163          * custom transformation.
164          */
165         template< class transformation >
image(std::istream & stream,transformation const & transform)166         image(std::istream& stream, transformation const& transform)
167         {
168             read_stream(stream, transform);
169         }
170 
171         /**
172          * \brief Reads an image from specified file using default
173          * converting transform.
174          */
read(std::string const & filename)175         void read(std::string const& filename)
176         {
177             read(filename, transform_convert());
178         }
179 
180         /**
181          * \brief Reads an image from specified file using custom
182          * transformaton.
183          */
184         template< class transformation >
read(std::string const & filename,transformation const & transform)185         void read(std::string const& filename, transformation const& transform)
186         {
187             read(filename.c_str(), transform);
188         }
189 
190         /**
191          * \brief Reads an image from specified file using default
192          * converting transform.
193          */
read(char const * filename)194         void read(char const* filename)
195         {
196             read(filename, transform_convert());
197         }
198 
199         /**
200          * \brief Reads an image from specified file using custom
201          * transformaton.
202          */
203         template< class transformation >
read(char const * filename,transformation const & transform)204         void read(char const* filename, transformation const& transform)
205         {
206             std::ifstream stream(filename, std::ios::binary);
207             if (!stream.is_open())
208             {
209                 throw std_error(filename);
210             }
211             stream.exceptions(std::ios::badbit);
212             read_stream(stream, transform);
213         }
214 
215         /**
216          * \brief Reads an image from a stream using default
217          * converting transform.
218          */
read(std::istream & stream)219         void read(std::istream& stream)
220         {
221             read_stream(stream, transform_convert());
222         }
223 
224         /**
225          * \brief Reads an image from a stream using custom
226          * transformation.
227          */
228         template< class transformation >
read(std::istream & stream,transformation const & transform)229         void read(std::istream& stream, transformation const& transform)
230         {
231             read_stream(stream, transform);
232         }
233 
234         /**
235          * \brief Reads an image from a stream using default
236          * converting transform.
237          */
238         template< class istream >
read_stream(istream & stream)239         void read_stream(istream& stream)
240         {
241             read_stream(stream, transform_convert());
242         }
243 
244         /**
245          * \brief Reads an image from a stream using custom
246          * transformation.
247          */
248         template< class istream, class transformation >
read_stream(istream & stream,transformation const & transform)249         void read_stream(istream& stream, transformation const& transform)
250         {
251             pixel_consumer pixcon(m_info, m_pixbuf);
252             pixcon.read(stream, transform);
253         }
254 
255         /**
256          * \brief Writes an image to specified file.
257          */
write(std::string const & filename)258         void write(std::string const& filename)
259         {
260             write(filename.c_str());
261         }
262 
263         /**
264          * \brief Writes an image to specified file.
265          */
write(char const * filename)266         void write(char const* filename)
267         {
268             std::ofstream stream(filename, std::ios::binary);
269             if (!stream.is_open())
270             {
271                 throw std_error(filename);
272             }
273             stream.exceptions(std::ios::badbit);
274             write_stream(stream);
275         }
276 
277         /**
278          * \brief Writes an image to a stream.
279          */
280         template< class ostream >
write_stream(ostream & stream)281         void write_stream(ostream& stream)
282         {
283             pixel_generator pixgen(m_info, m_pixbuf);
284             pixgen.write(stream);
285         }
286 
287         /**
288          * \brief Returns a reference to image pixel buffer.
289          */
get_pixbuf()290         pixbuf& get_pixbuf()
291         {
292             return m_pixbuf;
293         }
294 
295         /**
296          * \brief Returns a const reference to image pixel buffer.
297          */
get_pixbuf() const298         pixbuf const& get_pixbuf() const
299         {
300             return m_pixbuf;
301         }
302 
303         /**
304          * \brief Replaces the image pixel buffer.
305          *
306          * \param buffer  a pixel buffer object to take a copy from
307          */
set_pixbuf(pixbuf const & buffer)308         void set_pixbuf(pixbuf const& buffer)
309         {
310             m_pixbuf = buffer;
311         }
312 
get_width() const313         uint_32 get_width() const
314         {
315             return m_pixbuf.get_width();
316         }
317 
get_height() const318         uint_32 get_height() const
319         {
320             return m_pixbuf.get_height();
321         }
322 
323         /**
324          * \brief Resizes the image pixel buffer.
325          */
resize(uint_32 width,uint_32 height)326         void resize(uint_32 width, uint_32 height)
327         {
328             m_pixbuf.resize(width, height);
329             m_info.set_width(width);
330             m_info.set_height(height);
331         }
332 
333         /**
334          * \brief Returns a reference to the row of image data at
335          * specified index.
336          *
337          * \see pixel_buffer::get_row()
338          */
get_row(size_t index)339         row_access get_row(size_t index)
340         {
341             return m_pixbuf.get_row(index);
342         }
343 
344         /**
345          * \brief Returns a const reference to the row of image data at
346          * specified index.
347          *
348          * \see pixel_buffer::get_row()
349          */
get_row(size_t index) const350         row_const_access get_row(size_t index) const
351         {
352             return m_pixbuf.get_row(index);
353         }
354 
355         /**
356          * \brief The non-checking version of get_row() method.
357          */
operator [](size_t index)358         row_access operator[](size_t index)
359         {
360             return m_pixbuf[index];
361         }
362 
363         /**
364          * \brief The non-checking version of get_row() method.
365          */
operator [](size_t index) const366         row_const_access operator[](size_t index) const
367         {
368             return m_pixbuf[index];
369         }
370 
371         /**
372          * \brief Returns a pixel at (x,y) position.
373          */
get_pixel(size_t x,size_t y) const374         pixel get_pixel(size_t x, size_t y) const
375         {
376             return m_pixbuf.get_pixel(x, y);
377         }
378 
379         /**
380          * \brief Replaces a pixel at (x,y) position.
381          */
set_pixel(size_t x,size_t y,pixel p)382         void set_pixel(size_t x, size_t y, pixel p)
383         {
384             m_pixbuf.set_pixel(x, y, p);
385         }
386 
get_interlace_type() const387         interlace_type get_interlace_type() const
388         {
389             return m_info.get_interlace_type();
390         }
391 
set_interlace_type(interlace_type interlace)392         void set_interlace_type(interlace_type interlace)
393         {
394             m_info.set_interlace_type(interlace);
395         }
396 
get_compression_type() const397         compression_type get_compression_type() const
398         {
399             return m_info.get_compression_type();
400         }
401 
set_compression_type(compression_type compression)402         void set_compression_type(compression_type compression)
403         {
404             m_info.set_compression_type(compression);
405         }
406 
get_filter_type() const407         filter_type get_filter_type() const
408         {
409             return m_info.get_filter_type();
410         }
411 
set_filter_type(filter_type filter)412         void set_filter_type(filter_type filter)
413         {
414             m_info.set_filter_type(filter);
415         }
416 
417         /**
418          * \brief Returns a reference to the image palette.
419          */
get_palette()420         palette& get_palette()
421         {
422             return m_info.get_palette();
423         }
424 
425         /**
426          * \brief Returns a const reference to the image palette.
427          */
get_palette() const428         palette const& get_palette() const
429         {
430             return m_info.get_palette();
431         }
432 
433         /**
434          * \brief Replaces the image palette.
435          */
set_palette(palette const & plte)436         void set_palette(palette const& plte)
437         {
438             m_info.set_palette(plte);
439         }
440 
get_tRNS() const441         tRNS const& get_tRNS() const
442         {
443             return m_info.get_tRNS();
444         }
445 
get_tRNS()446         tRNS& get_tRNS()
447         {
448             return m_info.get_tRNS();
449         }
450 
set_tRNS(tRNS const & trns)451         void set_tRNS(tRNS const& trns)
452         {
453             m_info.set_tRNS(trns);
454         }
455 
get_gamma() const456         double get_gamma() const
457         {
458             return m_info.get_gamma();
459         }
460 
set_gamma(double gamma)461         void set_gamma(double gamma)
462         {
463             m_info.set_gamma(gamma);
464         }
465 
466     protected:
467         /**
468          * \brief A common base class template for pixel_consumer and
469          * pixel_generator classes.
470          */
471         template< typename base_impl >
472         class streaming_impl
473             : public base_impl
474         {
475         public:
streaming_impl(image_info & info,pixbuf & pixels)476             streaming_impl(image_info& info, pixbuf& pixels)
477                 : base_impl(info),
478                   m_pixbuf(pixels)
479             {
480             }
481 
482             /**
483              * \brief Returns the starting address of a \c pos-th row
484              * in the image's pixel buffer.
485              */
get_next_row(size_t pos)486             byte* get_next_row(size_t pos)
487             {
488                 typedef typename pixbuf::row_traits row_traits;
489                 return reinterpret_cast< byte* >
490                     (row_traits::get_data(m_pixbuf.get_row(pos)));
491             }
492 
493         protected:
494             pixbuf& m_pixbuf;
495         };
496 
497         /**
498          * \brief The pixel buffer adapter for reading pixel data.
499          */
500         class pixel_consumer
501             : public streaming_impl< consumer< pixel,
502                                                pixel_consumer,
503                                                image_info_ref_holder,
504                                                /* interlacing = */ true > >
505         {
506         public:
pixel_consumer(image_info & info,pixbuf & pixels)507             pixel_consumer(image_info& info, pixbuf& pixels)
508                 : streaming_impl< consumer< pixel,
509                                             pixel_consumer,
510                                             image_info_ref_holder,
511                                             true > >(info, pixels)
512             {
513             }
514 
reset(size_t pass)515             void reset(size_t pass)
516             {
517                 if (pass == 0)
518                 {
519                     this->m_pixbuf.resize(this->get_info().get_width(),
520                                           this->get_info().get_height());
521                 }
522             }
523         };
524 
525         /**
526          * \brief The pixel buffer adapter for writing pixel data.
527          */
528         class pixel_generator
529             : public streaming_impl< generator< pixel,
530                                                 pixel_generator,
531                                                 image_info_ref_holder,
532                                                 /* interlacing = */ true > >
533         {
534         public:
pixel_generator(image_info & info,pixbuf & pixels)535             pixel_generator(image_info& info, pixbuf& pixels)
536                 : streaming_impl< generator< pixel,
537                                              pixel_generator,
538                                              image_info_ref_holder,
539                                              true > >(info, pixels)
540             {
541             }
542         };
543 
544         image_info m_info;
545         pixbuf m_pixbuf;
546     };
547 
548 } // namespace png
549 
550 #endif // PNGPP_IMAGE_HPP_INCLUDED
551