png.cpp

00001 
00002 /***************************************************************************
00003  *  png.cpp - PNG Reader
00004  *
00005  *  Created: Thu Apr 03 12:56:56 2008
00006  *  Copyright  2005-2008  Tim Niemueller [www.niemueller.de]
00007  *
00008  ****************************************************************************/
00009 
00010 /*  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version. A runtime exception applies to
00014  *  this software (see LICENSE.GPL_WRE file mentioned below for details).
00015  *
00016  *  This program is distributed in the hope that it will be useful,
00017  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  *  GNU Library General Public License for more details.
00020  *
00021  *  Read the full text in the LICENSE.GPL_WRE file in the doc directory.
00022  */
00023 
00024 #include <core/exception.h>
00025 #include <fvutils/readers/png.h>
00026 #include <fvutils/color/rgbyuv.h>
00027 
00028 #include <cstdio>
00029 #include <cstdlib>
00030 #include <png.h>
00031 
00032 using namespace fawkes;
00033 
00034 namespace firevision {
00035 #if 0 /* just to make Emacs auto-indent happy */
00036 }
00037 #endif
00038 
00039 /// @cond INTERNALS
00040 class PNGReaderData
00041 {
00042  public:
00043   FILE *infile;
00044   png_structp png_ptr;
00045   png_infop info_ptr;
00046   int number_passes;
00047   bool read;
00048 };
00049 /// @endcond
00050 
00051 /** @class PNGReader <fvutils/readers/png.h>
00052  * PNG file reader.
00053  * @author Tim Niemueller
00054  */
00055 
00056 /** Constructor.
00057  * @param filename file to read
00058  */
00059 PNGReader::PNGReader(const char *filename)
00060 {
00061   opened = false;
00062   buffer = NULL;
00063 
00064   __d = setup_read(filename);
00065 
00066   opened = true;
00067 }
00068 
00069 
00070 PNGReaderData *
00071 PNGReader::setup_read(const char *filename)
00072 {
00073   PNGReaderData *d = new PNGReaderData();
00074   d->read = false;
00075 
00076   if ((d->infile = fopen(filename, "rb")) == NULL) {
00077     throw Exception("Cannot open PNG file");
00078   }
00079 
00080   d->png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
00081 
00082   if (d->png_ptr == NULL) {
00083     fclose(d->infile);
00084     throw Exception("Could not create PNG read struct");
00085   }
00086 
00087   /* Allocate/initialize the memory for image information.  REQUIRED. */
00088   d->info_ptr = png_create_info_struct(d->png_ptr);
00089   if (d->info_ptr == NULL) {
00090     fclose(d->infile);
00091     png_destroy_read_struct(&d->png_ptr, (png_infopp)NULL, (png_infopp)NULL);
00092     throw Exception("Could not create PNG info struct");
00093   }
00094 
00095   /* Set error handling if you are using the setjmp/longjmp method (this is
00096    * the normal method of doing things with libpng).  REQUIRED unless you
00097    * set up your own error handlers in the png_create_read_struct() earlier.
00098    */
00099   if (setjmp(png_jmpbuf(d->png_ptr))) {
00100     /* Free all of the memory associated with the png_ptr and info_ptr */
00101     png_destroy_read_struct(&d->png_ptr, &d->info_ptr, (png_infopp)NULL);
00102     fclose(d->infile);
00103     /* If we get here, we had a problem reading the file */
00104     throw Exception("Could not read PNG file");
00105   }
00106 
00107   /* Set up the input control if you are using standard C streams */
00108   png_init_io(d->png_ptr, d->infile);
00109 
00110   /* The call to png_read_info() gives us all of the information from the
00111    * PNG file before the first IDAT (image data chunk).  REQUIRED */
00112   png_read_info(d->png_ptr, d->info_ptr);
00113   
00114   /* tell libpng to strip 16 bit/color files down to 8 bits/color */
00115   png_set_strip_16(d->png_ptr);
00116 
00117   /* Strip alpha bytes from the input data without combining with the
00118    * background (not recommended). */
00119   png_set_strip_alpha(d->png_ptr);
00120 
00121   /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single
00122    * byte into separate bytes (useful for paletted and grayscale images). */
00123   png_set_packing(d->png_ptr);
00124 
00125   png_byte color_type = png_get_color_type(d->png_ptr, d->info_ptr);
00126 
00127   /* Expand paletted colors into true RGB triplets */
00128   if (color_type == PNG_COLOR_TYPE_PALETTE)  png_set_palette_to_rgb(d->png_ptr);
00129 
00130   /* Expand grayscale images into true RGB triplets */
00131   if (color_type == PNG_COLOR_TYPE_GRAY)     png_set_gray_to_rgb(d->png_ptr);
00132 
00133 
00134   /* Tell libpng to handle the gamma conversion for you.  The final call
00135    * is a good guess for PC generated images, but it should be configurable
00136    * by the user at run time by the user.  It is strongly suggested that
00137    * your application support gamma correction. */
00138   int intent;
00139   double screen_gamma = 2.2;  /* A good guess for a PC monitors in a dimly lit room */
00140   if (png_get_sRGB(d->png_ptr, d->info_ptr, &intent)) {
00141     png_set_gamma(d->png_ptr, screen_gamma, 0.45455);
00142   } else {
00143     double image_gamma;
00144     if (png_get_gAMA(d->png_ptr, d->info_ptr, &image_gamma)) {
00145       png_set_gamma(d->png_ptr, screen_gamma, image_gamma);
00146     } else {
00147       png_set_gamma(d->png_ptr, screen_gamma, 0.45455);
00148     }
00149   }
00150 
00151   /* Turn on interlace handling.  REQUIRED if you are not using
00152    * png_read_image().  To see how to handle interlacing passes,
00153    * see the png_read_row() method below: */
00154   d->number_passes = png_set_interlace_handling(d->png_ptr);
00155 
00156   /* Optional call to gamma correct and add the background to the palette
00157    * and update info structure.  REQUIRED if you are expecting libpng to
00158    * update the palette for you (ie you selected such a transform above). */
00159   png_read_update_info(d->png_ptr, d->info_ptr);
00160 
00161   return d;
00162 }
00163 
00164 /** Destructor. */
00165 PNGReader::~PNGReader()
00166 {
00167   fclose( __d->infile );
00168   /* clean up after the read, and free any memory allocated - REQUIRED */
00169   png_destroy_read_struct(&__d->png_ptr, &__d->info_ptr, (png_infopp)NULL);
00170 
00171   delete __d;
00172 
00173   opened = false;
00174 }
00175 
00176 
00177 void
00178 PNGReader::set_buffer(unsigned char *yuv422planar_buffer)
00179 {
00180   buffer = yuv422planar_buffer;
00181 }
00182 
00183 
00184 colorspace_t
00185 PNGReader::colorspace()
00186 {
00187   return YUV422_PLANAR;
00188 }
00189 
00190 
00191 unsigned int
00192 PNGReader::pixel_width()
00193 {
00194   if ( opened ) {
00195     return png_get_image_width(__d->png_ptr, __d->info_ptr);
00196   } else {
00197     return 0;
00198   }
00199 }
00200 
00201 
00202 unsigned int
00203 PNGReader::pixel_height()
00204 {
00205   if ( opened ) {
00206     return png_get_image_height(__d->png_ptr, __d->info_ptr);
00207   } else {
00208     return 0;
00209   }
00210 }
00211 
00212 
00213 void
00214 PNGReader::read()
00215 {
00216   if ( buffer == NULL ) {
00217     throw Exception("PNGReader::read: buffer == NULL");
00218   }
00219   if ( __d->read ) {
00220     throw Exception("Can read PNG file only once.");
00221   }
00222   __d->read = true;
00223 
00224   png_bytep row_pointer;
00225   row_pointer = (png_bytep)png_malloc(__d->png_ptr, png_get_rowbytes(__d->png_ptr, __d->info_ptr));
00226 
00227   unsigned int lheight = pixel_height();
00228   unsigned int lwidth  = pixel_width();
00229 
00230   for (int pass = 0; pass < __d->number_passes; ++pass) {
00231     for (unsigned y = 0; y < lheight; ++y) {
00232       png_read_rows(__d->png_ptr, &row_pointer, (png_bytepp)NULL, 1);
00233       convert_line_rgb_to_yuv422planar( row_pointer, buffer, lwidth, lheight, 0, y );
00234     }
00235   }
00236 
00237   /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
00238   png_read_end(__d->png_ptr, __d->info_ptr);
00239   png_free(__d->png_ptr, row_pointer);
00240 
00241 }
00242 
00243 } // end namespace firevision