pngparser.cc

Go to the documentation of this file.
00001 
00003 
00004 //
00005 // Copyright (c) 2002-2004 California Institute of Technology
00006 // Copyright (c) 2004-2007 University of Southern California
00007 // Rob Peters <rjpeters at usc dot edu>
00008 //
00009 // created: Wed Apr 24 20:05:06 2002
00010 // commit: $Id: pngparser.cc 10065 2007-04-12 05:54:56Z rjpeters $
00011 // $HeadURL: file:///lab/rjpeters/svnrepo/code/trunk/groovx/src/media/pngparser.cc $
00012 //
00013 // --------------------------------------------------------------------
00014 //
00015 // This file is part of GroovX.
00016 //   [http://ilab.usc.edu/rjpeters/groovx/]
00017 //
00018 // GroovX is free software; you can redistribute it and/or modify it
00019 // under the terms of the GNU General Public License as published by
00020 // the Free Software Foundation; either version 2 of the License, or
00021 // (at your option) any later version.
00022 //
00023 // GroovX is distributed in the hope that it will be useful, but
00024 // WITHOUT ANY WARRANTY; without even the implied warranty of
00025 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00026 // General Public License for more details.
00027 //
00028 // You should have received a copy of the GNU General Public License
00029 // along with GroovX; if not, write to the Free Software Foundation,
00030 // Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
00031 //
00033 
00034 #ifndef GROOVX_MEDIA_PNGPARSER_CC_UTC20050626084018_DEFINED
00035 #define GROOVX_MEDIA_PNGPARSER_CC_UTC20050626084018_DEFINED
00036 
00037 #include "pngparser.h"
00038 
00039 #ifndef HAVE_LIBPNG
00040 
00041 #include "rutz/error.h"
00042 
00043 void media::load_png(const char* /*filename*/, media::bmap_data& /*data*/)
00044 {
00045   throw rutz::error("png image files are not supported in this build",
00046                     SRC_POS);
00047 }
00048 
00049 void media::save_png(const char* /*filename*/, const media::bmap_data& /*data*/)
00050 {
00051   throw rutz::error("png image files are not supported in this build",
00052                     SRC_POS);
00053 }
00054 
00055 #else
00056 
00057 #include "geom/vec2.h"
00058 
00059 #include "media/bmapdata.h"
00060 
00061 #include "rutz/arrays.h"
00062 #include "rutz/error.h"
00063 #include "rutz/sfmt.h"
00064 
00065 #include <cstdio>
00066 #include <png.h>
00067 
00068 #include "rutz/debug.h"
00069 GVX_DBG_REGISTER
00070 #include "rutz/trace.h"
00071 
00072 namespace
00073 {
00075 
00078   class png_parser
00079   {
00080   public:
00081     png_parser() :
00082       m_file(0),
00083       m_png_ptr(0),
00084       m_info_ptr(0),
00085       m_end_ptr(0)
00086     {}
00087 
00088     ~png_parser() { close(); }
00089 
00090     void close();
00091 
00092     void parse(const char* filename, media::bmap_data& data);
00093 
00094   private:
00095     png_parser(const png_parser&);
00096     png_parser& operator=(const png_parser&);
00097 
00098     FILE* m_file;
00099     png_structp m_png_ptr;
00100     png_infop m_info_ptr;
00101     png_infop m_end_ptr;
00102   };
00103 
00104   void png_parser::close()
00105   {
00106     GVX_TRACE("png_parser::close");
00107     if (m_png_ptr != 0)
00108       {
00109         png_destroy_read_struct(m_png_ptr ? &m_png_ptr : 0,
00110                                 m_info_ptr ? &m_info_ptr : 0,
00111                                 m_end_ptr ? &m_end_ptr : 0);
00112       }
00113 
00114     if (m_file != 0)
00115       {
00116         fclose(m_file);
00117         m_file = 0;
00118       }
00119   }
00120 
00121   void png_parser::parse(const char* filename, media::bmap_data& data)
00122   {
00123     GVX_TRACE("png_parser::parse");
00124     m_file = fopen(filename, "rb");
00125     if (m_file == 0)
00126       {
00127         throw rutz::error("couldn't open file for png reading", SRC_POS);
00128       }
00129 
00130     const int nheader = 8;
00131     png_byte header[nheader];
00132 
00133     fread(header, 1, nheader, m_file);
00134 
00135     int is_png = !png_sig_cmp(header, 0, nheader);
00136     if (!is_png)
00137       {
00138         throw rutz::error(rutz::sfmt("%s is not a PNG image file",
00139                                      filename),
00140                           SRC_POS);
00141       }
00142 
00143     m_png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
00144     if (m_png_ptr == 0)
00145       throw rutz::error("PNG library couldn't create read_struct",
00146                         SRC_POS);
00147 
00148     m_info_ptr = png_create_info_struct(m_png_ptr);
00149     if (m_info_ptr == 0)
00150       {
00151         throw rutz::error("PNG library couldn't create info_struct",
00152                           SRC_POS);
00153       }
00154 
00155     m_end_ptr = png_create_info_struct(m_png_ptr);
00156     if (m_end_ptr == 0)
00157       {
00158         throw rutz::error("PNG library couldn't create end info_struct",
00159                           SRC_POS);
00160       }
00161 
00162     png_init_io(m_png_ptr, m_file);
00163 
00164     png_set_sig_bytes(m_png_ptr, nheader);
00165 
00166     png_read_info(m_png_ptr, m_info_ptr);
00167 
00168     const int bit_depth = png_get_bit_depth(m_png_ptr, m_info_ptr);
00169 
00170     switch (bit_depth)
00171       {
00172       case 16:
00173         // Strip 16-bit pixels down to 8-bit
00174         png_set_strip_16(m_png_ptr);
00175         break;
00176 
00177       case 8:
00178         // nothing
00179         break;
00180 
00181       case 4:
00182       case 2:
00183       case 1:
00184         // Forces 1-, 2-, or 4-bit pixels to be expanded into full bytes.
00185         png_set_packing(m_png_ptr);
00186         break;
00187 
00188       default:
00189         throw rutz::error(rutz::sfmt("bit-depth '%d' is not supported "
00190                                      "for PNG images "
00191                                      "(must be 8- or 16-bit)",
00192                                      bit_depth),
00193                           SRC_POS);
00194       }
00195 
00196     const png_byte color_type = png_get_color_type(m_png_ptr, m_info_ptr);
00197 
00198     if (color_type & PNG_COLOR_MASK_ALPHA)
00199       png_set_strip_alpha(m_png_ptr);
00200 
00201     if (color_type & PNG_COLOR_MASK_PALETTE)
00202       png_set_palette_to_rgb(m_png_ptr);
00203 
00204     // This must come after any+all transformations are specified
00205     png_read_update_info(m_png_ptr, m_info_ptr);
00206 
00207     // These calls must come after read_update_info, so that we get values
00208     // that reflect any transformations
00209     const int width = png_get_image_width(m_png_ptr, m_info_ptr);
00210     const int height = png_get_image_height(m_png_ptr, m_info_ptr);
00211 
00212     dbg_eval(3, width); dbg_eval_nl(3, height);
00213 
00214     const int row_bytes = png_get_rowbytes(m_png_ptr, m_info_ptr);
00215 
00216     dbg_eval_nl(3, row_bytes);
00217 
00218     const int nchannels = png_get_channels(m_png_ptr, m_info_ptr);
00219 
00220     dbg_eval_nl(3, nchannels);
00221 
00222     GVX_ASSERT(row_bytes == width*nchannels);
00223 
00224     media::bmap_data new_data(geom::vec2<int>(width, height),
00225                               nchannels*8, // bits_per_pixel
00226                               1); // byte_alignment
00227 
00228     rutz::fixed_block<png_bytep> row_pointers(height);
00229 
00230     for (int i = 0; i < height; ++i)
00231       {
00232         row_pointers[i] = (png_bytep) (new_data.row_ptr(i));
00233       }
00234 
00235     png_read_image(m_png_ptr, &row_pointers[0]);
00236 
00237     png_read_end(m_png_ptr, m_end_ptr);
00238 
00239     data.swap(new_data);
00240 
00241     this->close();
00242   }
00243 
00244   class png_writer
00245   {
00246   public:
00247     png_writer() :
00248       m_file(0),
00249       m_png_ptr(0),
00250       m_info_ptr(0)
00251     {}
00252 
00253     ~png_writer() { close(); }
00254 
00255     void close();
00256 
00257     void write(const char* filename, const media::bmap_data& data);
00258 
00259   private:
00260     png_writer(const png_writer&);
00261     png_writer& operator=(const png_writer&);
00262 
00263     FILE* m_file;
00264     png_structp m_png_ptr;
00265     png_infop m_info_ptr;
00266   };
00267 
00268   void png_writer::close()
00269   {
00270     GVX_TRACE("png_writer::close");
00271     if (m_png_ptr != 0)
00272       {
00273         png_destroy_write_struct(m_png_ptr ? &m_png_ptr : 0,
00274                                  m_info_ptr ? &m_info_ptr : 0);
00275       }
00276 
00277     if (m_file != 0)
00278       {
00279         fclose(m_file);
00280         m_file = 0;
00281       }
00282   }
00283 
00284   int get_color_type(const media::bmap_data& data)
00285   {
00286     switch (data.bits_per_pixel())
00287       {
00288       case 1: return PNG_COLOR_TYPE_GRAY;
00289       case 8: return PNG_COLOR_TYPE_GRAY;
00290       case 24: return PNG_COLOR_TYPE_RGB;
00291       case 32: return PNG_COLOR_TYPE_RGB_ALPHA;
00292       default:
00293         throw rutz::error(rutz::sfmt("invalid bits_per_pixel value: %d",
00294                                      data.bits_per_pixel()), SRC_POS);
00295       }
00296     return 0; // can't happen, but placate compiler
00297   }
00298 
00299   void png_writer::write(const char* filename,
00300                          const media::bmap_data& data)
00301   {
00302     GVX_TRACE("png_writer::write");
00303 
00304     m_file = fopen(filename, "wb");
00305     if (m_file == 0)
00306       {
00307         throw rutz::error(rutz::sfmt("couldn't open file '%s' for "
00308                                      "png writing", filename), SRC_POS);
00309       }
00310 
00311     m_png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
00312     if (m_png_ptr == 0)
00313       throw rutz::error("PNG library couldn't create write_struct",
00314                         SRC_POS);
00315 
00316     m_info_ptr = png_create_info_struct(m_png_ptr);
00317     if (m_info_ptr == 0)
00318       {
00319         throw rutz::error("PNG library couldn't create info_struct",
00320                           SRC_POS);
00321       }
00322 
00323     png_init_io(m_png_ptr, m_file);
00324 
00325     png_set_compression_level(m_png_ptr, 7);
00326 
00327     png_set_IHDR(m_png_ptr, m_info_ptr,
00328                  data.width(), data.height(),
00329                  data.bits_per_component(),
00330                  get_color_type(data),
00331                  PNG_INTERLACE_NONE,
00332                  PNG_COMPRESSION_TYPE_DEFAULT,
00333                  PNG_FILTER_TYPE_DEFAULT);
00334 
00335     png_write_info(m_png_ptr, m_info_ptr);
00336 
00337     const int height = data.height();
00338 
00339     rutz::fixed_block<png_bytep> row_pointers(height);
00340 
00341     for (int i = 0; i < height; ++i)
00342       {
00343         row_pointers[i] = static_cast<png_bytep>(data.row_ptr(i));
00344       }
00345 
00346     png_write_image(m_png_ptr, &row_pointers[0]);
00347     png_write_end(m_png_ptr, m_info_ptr);
00348 
00349     this->close();
00350   }
00351 
00352 } // end anonymous namespace
00353 
00354 void media::load_png(const char* filename, media::bmap_data& data)
00355 {
00356 GVX_TRACE("media::load_png");
00357   png_parser parser;
00358 
00359   parser.parse(filename, data);
00360 }
00361 
00362 void media::save_png(const char* filename, const media::bmap_data& data)
00363 {
00364 GVX_TRACE("media::save_png");
00365   png_writer writer;
00366 
00367   writer.write(filename, data);
00368 }
00369 
00370 #endif // HAVE_LIBPNG
00371 
00372 static const char __attribute__((used)) vcid_groovx_media_pngparser_cc_utc20050626084018[] = "$Id: pngparser.cc 10065 2007-04-12 05:54:56Z rjpeters $ $HeadURL: file:
00373 #endif // !GROOVX_MEDIA_PNGPARSER_CC_UTC20050626084018_DEFINED

The software described here is Copyright (c) 1998-2005, Rob Peters.
This page was generated Wed Dec 3 06:49:39 2008 by Doxygen version 1.5.5.