Claw  1.7.3
code/png_reader.cpp
Go to the documentation of this file.
00001 /*
00002   CLAW - a C++ Library Absolutely Wonderful
00003 
00004   CLAW is a free library without any particular aim but being useful to 
00005   anyone.
00006 
00007   Copyright (C) 2005-2011 Julien Jorge
00008 
00009   This library is free software; you can redistribute it and/or
00010   modify it under the terms of the GNU Lesser General Public
00011   License as published by the Free Software Foundation; either
00012   version 2.1 of the License, or (at your option) any later version.
00013 
00014   This library is distributed in the hope that it will be useful,
00015   but WITHOUT ANY WARRANTY; without even the implied warranty of
00016   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017   Lesser General Public License for more details.
00018 
00019   You should have received a copy of the GNU Lesser General Public
00020   License along with this library; if not, write to the Free Software
00021   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00022 
00023   contact: julien.jorge@gamned.org
00024 */
00030 #include <claw/png.hpp>
00031 
00032 #include <claw/exception.hpp>
00033 #include <claw/assert.hpp>
00034 
00035 #include <limits>
00036 
00037 /*----------------------------------------------------------------------------*/
00044 void claw__graphic__png__source_manager__read
00045 ( png_structp png_ptr, png_bytep data, png_size_t length )
00046 {
00047   claw::graphic::png::reader::source_manager* self =
00048     (claw::graphic::png::reader::source_manager*)png_get_io_ptr(png_ptr);
00049 
00050   self->read(data, length);
00051 } // claw__graphic__png__source_manager__read()
00052 
00053 
00054 
00055 
00056 /*----------------------------------------------------------------------------*/
00061 claw::graphic::png::reader::source_manager::source_manager( std::istream& is )
00062   : m_input(is)
00063 {
00064   CLAW_PRECOND( !!is );
00065 } // png::reader::source_manager::source_manager()
00066 
00067 /*----------------------------------------------------------------------------*/
00073 void claw::graphic::png::reader::source_manager::read
00074 ( png_bytep data, png_size_t length )
00075 {
00076   m_input.read( (char*)data, length * sizeof(png_byte) );
00077 } // png::reader::source_manager::read()
00078 
00079 
00080 
00081 
00082 /*----------------------------------------------------------------------------*/
00083 const unsigned int claw::graphic::png::reader::s_rgba_pixel_size = 4;
00084 
00085 /*----------------------------------------------------------------------------*/
00090 claw::graphic::png::reader::reader( image& img )
00091   : m_image( img )
00092 {
00093 
00094 } // png::reader::reader()
00095 
00096 /*----------------------------------------------------------------------------*/
00103 claw::graphic::png::reader::reader( image& img, std::istream& f )
00104   : m_image( img )
00105 {
00106   load(f);
00107 } // png::reader::reader()
00108 
00109 /*----------------------------------------------------------------------------*/
00114 void claw::graphic::png::reader::load( std::istream& f )
00115 {
00116   CLAW_PRECOND( !!f );
00117 
00118   std::istream::pos_type init_pos = f.tellg();
00119 
00120   try
00121     {
00122       read_from_file(f);
00123     }
00124   catch(...)
00125     {
00126       f.clear();
00127       f.seekg( init_pos, std::ios_base::beg );
00128       throw;
00129     }
00130 } // png::reader::load()
00131 
00132 /*----------------------------------------------------------------------------*/
00137 void claw::graphic::png::reader::read_from_file( std::istream& f )
00138 {
00139   source_manager infile(f);
00140   png_structp png_ptr;
00141   png_infop info_ptr;
00142 
00143   create_read_structures(png_ptr, info_ptr);
00144 
00145   if (setjmp(png_jmpbuf(png_ptr)))
00146     {
00147       /* If we get here, we had a problem reading the file */
00148       /* Free all of the memory associated with the png_ptr and info_ptr */
00149       png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
00150       throw CLAW_EXCEPTION("Invalid PNG file.");
00151     }
00152       
00153   check_if_png( png_ptr, f );
00154 
00155   png_set_read_fn( png_ptr, (void *)&infile,
00156                    claw__graphic__png__source_manager__read );
00157 
00158   png_read_info(png_ptr, info_ptr);
00159 
00160   png_set_strip_16(png_ptr);
00161   png_set_expand_gray_1_2_4_to_8(png_ptr);
00162   png_set_packing(png_ptr);
00163 
00164   png_set_tRNS_to_alpha(png_ptr);
00165 
00166   // transform palette index into RGB value
00167   png_set_palette_to_rgb(png_ptr);
00168   
00169   // add an alpha value if none
00170   png_set_filler( png_ptr,
00171                   std::numeric_limits<rgba_pixel_8::component_type>::max(),
00172                   PNG_FILLER_AFTER );
00173 
00174   png_read_update_info(png_ptr, info_ptr);
00175 
00176   read_image( png_ptr, info_ptr );
00177       
00178   png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
00179 } // png::reader::read_from_file()
00180 
00181 /*----------------------------------------------------------------------------*/
00187 void claw::graphic::png::reader::check_if_png
00188 ( png_structp png_ptr, std::istream& f ) const
00189 {
00190   CLAW_PRECOND( !!f );
00191 
00192   const unsigned int bytes_to_check = 8;
00193   png_byte buffer[bytes_to_check];
00194 
00195   /* Read in some of the signature bytes */
00196   f.read( (char*)buffer, bytes_to_check * sizeof(png_byte) );
00197 
00198   if ( (png_sig_cmp(buffer, (png_size_t)0, bytes_to_check) != 0) || !f )
00199     throw CLAW_EXCEPTION( "Not a PNG file." );
00200 
00201   png_set_sig_bytes(png_ptr, bytes_to_check);
00202 } // png::reader::check_if_png()
00203 
00204 /*----------------------------------------------------------------------------*/
00210 void claw::graphic::png::reader::read_image
00211 ( png_structp png_ptr, png_infop info_ptr )
00212 {
00213   CLAW_PRECOND( png_ptr );
00214   CLAW_PRECOND( info_ptr );
00215 
00216   m_image.set_size( png_get_image_width(png_ptr, info_ptr),
00217                     png_get_image_height(png_ptr, info_ptr) );
00218 
00219   if ( png_get_interlace_type(png_ptr, info_ptr) == PNG_INTERLACE_NONE )
00220     read_sequential_image(png_ptr, info_ptr);
00221   else
00222     read_interlaced_image( png_ptr, info_ptr,
00223                            png_set_interlace_handling(png_ptr) );
00224 } // png::reader::read_image()
00225 
00226 /*----------------------------------------------------------------------------*/
00232 void claw::graphic::png::reader::read_sequential_image
00233 ( png_structp png_ptr, png_infop info_ptr )
00234 {
00235   CLAW_PRECOND( png_ptr );
00236   CLAW_PRECOND( info_ptr );
00237 
00238   png_bytep data =
00239     (png_bytep)png_malloc( png_ptr, s_rgba_pixel_size * m_image.width() );
00240   const png_byte color_type( png_get_color_type(png_ptr, info_ptr) );
00241 
00242   try
00243     {
00244       for (unsigned int y=0; y!=m_image.height(); ++y)
00245         {
00246           png_read_row(png_ptr, data, NULL);
00247           copy_pixel_line( color_type, data, y );
00248         }
00249     }
00250   catch(...)
00251     {
00252       png_free(png_ptr, data);
00253       throw;
00254     }
00255 
00256   png_free(png_ptr, data);
00257 } // png::reader::read_sequential_image()
00258 
00259 /*----------------------------------------------------------------------------*/
00266 void claw::graphic::png::reader::read_interlaced_image
00267 ( png_structp png_ptr, png_infop info_ptr, unsigned int passes )
00268 {
00269   CLAW_PRECOND( passes > 1 );
00270   CLAW_PRECOND( png_ptr );
00271   CLAW_PRECOND( info_ptr );
00272 
00273   const unsigned int row_length = s_rgba_pixel_size * m_image.width();
00274   png_bytepp data =
00275     (png_bytepp)png_malloc( png_ptr, sizeof(png_bytep) * m_image.height() );
00276   unsigned int i=0;
00277   const png_byte color_type( png_get_color_type(png_ptr, info_ptr) );
00278 
00279   try
00280     {
00281       for (i=0; i!=m_image.height(); ++i)
00282         {
00283           data[i] = (png_bytep)png_malloc( png_ptr, row_length );
00284 
00285           if (!data[i])
00286             throw std::bad_alloc();
00287 
00288           copy_pixel_line( color_type, data[i], i );
00289         }
00290 
00291       for (unsigned int p=0; p!=passes; ++p)
00292         png_read_rows( png_ptr, data, NULL, m_image.height() );
00293 
00294       for (unsigned int y=0; y!=m_image.height(); ++y)
00295         copy_pixel_line( color_type, data[y], y );
00296     }
00297   catch(...)
00298     {
00299       for(unsigned int j=0; j!=i; ++j)
00300         png_free(png_ptr, data[j]);
00301 
00302       png_free(png_ptr, data);
00303       throw;
00304     }
00305 
00306   for(i=0; i!=m_image.height(); ++i)
00307     png_free(png_ptr, data[i]);
00308 
00309   png_free(png_ptr, data);
00310 } // png::reader::read_interlaced_image()
00311 
00312 /*----------------------------------------------------------------------------*/
00319 void
00320 claw::graphic::png::reader::copy_pixel_line
00321 ( png_byte color_type, png_bytep data, unsigned int y )
00322 {
00323   CLAW_PRECOND( data );
00324   CLAW_PRECOND( y < m_image.height() );
00325 
00326   if ( color_type == PNG_COLOR_TYPE_GRAY_ALPHA ) 
00327     // There is two bytes for each pixel in the line: the color and the opacity.
00328     for (unsigned int x=0; x!=m_image.width(); ++x, data += 2)
00329       {
00330         m_image[y][x].components.red   = data[0];
00331         m_image[y][x].components.green = data[0];
00332         m_image[y][x].components.blue  = data[0];
00333         m_image[y][x].components.alpha = data[1];
00334       }
00335   else
00336     // There is four bytes for each pixel in the line.
00337     for (unsigned int x=0; x!=m_image.width(); ++x, data+=s_rgba_pixel_size)
00338       {
00339         m_image[y][x].components.red   = data[0];
00340         m_image[y][x].components.green = data[1];
00341         m_image[y][x].components.blue  = data[2];
00342         m_image[y][x].components.alpha = data[3];
00343       }
00344 } // png::reader::copy_pixel_line()
00345 
00346 /*----------------------------------------------------------------------------*/
00352 void claw::graphic::png::reader::create_read_structures
00353 ( png_structp& png_ptr, png_infop& info_ptr ) const
00354 {
00355   png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
00356 
00357   if (png_ptr)
00358     {
00359       info_ptr = png_create_info_struct(png_ptr);
00360 
00361       if (!info_ptr)
00362         png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
00363     }
00364 
00365   if (!png_ptr || !info_ptr)
00366     throw CLAW_EXCEPTION("Can't create PNG read structures.");
00367 } // png::reader::create_read_structures()