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_CONSUMER_HPP_INCLUDED
32 #define PNGPP_CONSUMER_HPP_INCLUDED
33 
34 #include <cassert>
35 #include <stdexcept>
36 #include <iostream>
37 #include <istream>
38 
39 #include "config.hpp"
40 #include "error.hpp"
41 #include "streaming_base.hpp"
42 #include "reader.hpp"
43 #include "pixel_buffer.hpp"
44 
45 namespace png
46 {
47 
48     /**
49      * \brief Pixel consumer class template.
50      *
51      * Used as a base class for custom pixel consumer classes as well
52      * as inside image class implementation to read pixels into the
53      * pixel buffer.
54      *
55      * Encapsulates PNG %image reading procedure.  In order to create
56      * a custom pixel %consumer use CRTP trick:
57      *
58      * \code
59      * class pixel_consumer
60      *     : public png::consumer< pixel, pixel_consumer >
61      * {
62      *     ...
63      * };
64      * \endcode
65      *
66      * Your pixel %consumer class should implement \c get_next_row()
67      * method and \c reset() method (optional).  Their signatures are
68      * as follows:
69      *
70      * \code
71      * png::byte* get_next_row(png::uint_32 pos);
72      * void reset(size_t pass);
73      * \endcode
74      *
75      * The \c get_next_row() method is called every time a new row of
76      * %image data is available to the reader.  The position of the row
77      * being read is passed as \c pos parameter.  The \c pos takes
78      * values from \c 0 to \c <image_height>-1 inclusively.  The
79      * method should return the starting address of a row buffer
80      * capable of storing appropriate amount of pixels (i.e. the width
81      * of the %image being read).  The address should be casted to
82      * png::byte* pointer type using \c reinterpret_cast<> or a
83      * C-style cast.
84      *
85      * The optional \c reset() method is called every time the new
86      * pass of interlaced %image processing starts.  The number of
87      * interlace pass is avaiable as the only parameter of the method.
88      * For non-interlaced images the method is called once prior to
89      * any calls to \c get_next_row().  The value of \c 0 is passed
90      * for the \c pass number.
91      *
92      * An optional template parameter \c info_holder encapsulates
93      * image_info storage policy.  Using def_image_info_holder results
94      * in image_info object stored as a sub-object of the consumer
95      * class.  You may specify image_info_ref_holder in order to use a
96      * reference to the externally stored image_info object.  This way
97      * you will have to construct the consumer object passing the
98      * reference to image_info object.
99      *
100      * Also, you might want implement an %info holder object yourself
101      * to fine-tune your code.  In any case, you can access the
102      * image_info object from your %consumer class methods using the
103      * following code:
104      *
105      * \code
106      * png::image_info& info = m_info_holder.get_info();
107      * \endcode
108      *
109      * An optional \c bool template parameter \c interlacing_supported
110      * specifies whether reading interlacing images is supported by
111      * your %consumer class.  It defaults to \c false.  An attempt to
112      * read an interlaced %image will result in discarding pixels
113      * obtained at all the interlacing passes except the last one.
114      *
115      * In order to fully support interlacing specify \c true for \c
116      * interlacing_supported parameter and implement \c reset()
117      * method.
118      *
119      * \see image, generator
120      */
121     template< typename pixel,
122               class pixcon,
123               class info_holder = def_image_info_holder,
124               bool interlacing_supported = false >
125     class consumer
126         : public streaming_base< pixel, info_holder >
127     {
128     public:
129         typedef pixel_traits< pixel > traits;
130 
131         /**
132          * \brief The default io transformation: does nothing.
133          */
134         struct transform_identity
135         {
operator ()png::consumer::transform_identity136             void operator()(io_base&) const {}
137         };
138 
139         /**
140          * \brief Reads an image from the stream using default io
141          * transformation.
142          */
143         template< typename istream >
read(istream & stream)144         void read(istream& stream)
145         {
146             read(stream, transform_identity());
147         }
148 
149         /**
150          * \brief Reads an image from the stream using custom io
151          * transformation.
152          *
153          * Essentially, this method constructs a reader object and
154          * instructs it to read the image from the stream.  It handles
155          * IO transformation, as well as interlaced image reading.
156          */
157         template< typename istream, class transformation >
read(istream & stream,transformation const & transform)158         void read(istream& stream, transformation const& transform)
159         {
160             reader< istream > rd(stream);
161             rd.read_info();
162             transform(rd);
163 
164 #if __BYTE_ORDER == __LITTLE_ENDIAN
165             if (pixel_traits< pixel >::get_bit_depth() == 16)
166             {
167 #ifdef PNG_READ_SWAP_SUPPORTED
168                 rd.set_swap();
169 #else
170                 throw error("Cannot read 16-bit image: recompile with PNG_READ_SWAP_SUPPORTED.");
171 #endif
172             }
173 #endif
174 
175             // interlace handling _must_ be set up prior to info update
176             size_t pass_count;
177             if (rd.get_interlace_type() != interlace_none)
178             {
179 #ifdef PNG_READ_INTERLACING_SUPPORTED
180                 pass_count = rd.set_interlace_handling();
181 #else
182                 throw error("Cannot read interlaced image: interlace handling disabled.");
183 #endif
184             }
185             else
186             {
187                 pass_count = 1;
188             }
189 
190             rd.update_info();
191             if (rd.get_color_type() != traits::get_color_type()
192                 || rd.get_bit_depth() != traits::get_bit_depth())
193             {
194                 throw std::logic_error("color type and/or bit depth mismatch"
195                                        " in png::consumer::read()");
196             }
197 
198             this->get_info() = rd.get_image_info();
199 
200             pixcon* pixel_con = static_cast< pixcon* >(this);
201             if (pass_count > 1 && !interlacing_supported)
202             {
203                 skip_interlaced_rows(rd, pass_count);
204                 pass_count = 1;
205             }
206             read_rows(rd, pass_count, pixel_con);
207 
208             rd.read_end_info();
209         }
210 
211     protected:
212         typedef streaming_base< pixel, info_holder > base;
213 
214         /**
215          * \brief Constructs a consumer object using passed image_info
216          * object to store image information.
217          */
consumer(image_info & info)218         explicit consumer(image_info& info)
219             : base(info)
220         {
221         }
222 
223     private:
224         template< typename istream >
skip_interlaced_rows(reader<istream> & rd,size_t pass_count)225         void skip_interlaced_rows(reader< istream >& rd, size_t pass_count)
226         {
227             typedef std::vector< pixel > row;
228             typedef row_traits< row > row_traits_type;
229             row dummy_row(this->get_info().get_width());
230             for (size_t pass = 1; pass < pass_count; ++pass)
231             {
232                 rd.read_row(reinterpret_cast< byte* >
233                             (row_traits_type::get_data(dummy_row)));
234             }
235         }
236 
237         template< typename istream >
read_rows(reader<istream> & rd,size_t pass_count,pixcon * pixel_con)238         void read_rows(reader< istream >& rd, size_t pass_count,
239                        pixcon* pixel_con)
240         {
241             for (size_t pass = 0; pass < pass_count; ++pass)
242             {
243                 pixel_con->reset(pass);
244 
245                 for (uint_32 pos = 0; pos < this->get_info().get_height(); ++pos)
246                 {
247                     rd.read_row(pixel_con->get_next_row(pos));
248                 }
249             }
250         }
251     };
252 
253 } // namespace png
254 
255 #endif // PNGPP_CONSUMER_HPP_INCLUDED
256