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