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 /// @cond INTERNALS
00035 class PNGReaderData
00036 {
00037  public:
00038   FILE *infile;
00039   png_structp png_ptr;
00040   png_infop info_ptr;
00041   int number_passes;
00042   bool read;
00043 };
00044 /// @endcond
00045 
00046 /** @class PNGReader <fvutils/readers/png.h>
00047  * PNG file reader.
00048  * @author Tim Niemueller
00049  */
00050 
00051 /** Constructor.
00052  * @param filename file to read
00053  */
00054 PNGReader::PNGReader(const char *filename)
00055 {
00056   opened = false;
00057   buffer = NULL;
00058
00059   __d = setup_read(filename);
00060
00061   opened = true;
00062 }
00063
00064
00065 PNGReaderData *
00066 PNGReader::setup_read(const char *filename)
00067 {
00068   PNGReaderData *d = new PNGReaderData();
00069   d->read = false;
00070
00071   if ((d->infile = fopen(filename, "rb")) == NULL) {
00072     throw Exception("Cannot open PNG file");
00073   }
00074
00075   d->png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
00076
00077   if (d->png_ptr == NULL) {
00078     fclose(d->infile);
00079     throw Exception("Could not create PNG read struct");
00080   }
00081
00082   /* Allocate/initialize the memory for image information.  REQUIRED. */
00083   d->info_ptr = png_create_info_struct(d->png_ptr);
00084   if (d->info_ptr == NULL) {
00085     fclose(d->infile);
00086     png_destroy_read_struct(&d->png_ptr, png_infopp_NULL, png_infopp_NULL);
00087     throw Exception("Could not create PNG info struct");
00088   }
00089
00090   /* Set error handling if you are using the setjmp/longjmp method (this is
00091    * the normal method of doing things with libpng).  REQUIRED unless you
00092    * set up your own error handlers in the png_create_read_struct() earlier.
00093    */
00094   if (setjmp(png_jmpbuf(d->png_ptr))) {
00095     /* Free all of the memory associated with the png_ptr and info_ptr */
00096     png_destroy_read_struct(&d->png_ptr, &d->info_ptr, png_infopp_NULL);
00097     fclose(d->infile);
00098     /* If we get here, we had a problem reading the file */
00099     throw Exception("Could not read PNG file");
00100   }
00101
00102   /* Set up the input control if you are using standard C streams */
00103   png_init_io(d->png_ptr, d->infile);
00104
00105   /* The call to png_read_info() gives us all of the information from the
00106    * PNG file before the first IDAT (image data chunk).  REQUIRED */
00107   png_read_info(d->png_ptr, d->info_ptr);
00108
00109   /* tell libpng to strip 16 bit/color files down to 8 bits/color */
00110   png_set_strip_16(d->png_ptr);
00111
00112   /* Strip alpha bytes from the input data without combining with the
00113    * background (not recommended). */
00114   png_set_strip_alpha(d->png_ptr);
00115
00116   /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single
00117    * byte into separate bytes (useful for paletted and grayscale images). */
00118   png_set_packing(d->png_ptr);
00119
00120   png_byte color_type = png_get_color_type(d->png_ptr, d->info_ptr);
00121
00122   /* Expand paletted colors into true RGB triplets */
00123   if (color_type == PNG_COLOR_TYPE_PALETTE)  png_set_palette_to_rgb(d->png_ptr);
00124
00125   /* Expand grayscale images into true RGB triplets */
00126   if (color_type == PNG_COLOR_TYPE_GRAY)     png_set_gray_to_rgb(d->png_ptr);
00127
00128
00129   /* Tell libpng to handle the gamma conversion for you.  The final call
00130    * is a good guess for PC generated images, but it should be configurable
00131    * by the user at run time by the user.  It is strongly suggested that
00132    * your application support gamma correction. */
00133   int intent;
00134   double screen_gamma = 2.2;  /* A good guess for a PC monitors in a dimly lit room */
00135   if (png_get_sRGB(d->png_ptr, d->info_ptr, &intent)) {
00136     png_set_gamma(d->png_ptr, screen_gamma, 0.45455);
00137   } else {
00138     double image_gamma;
00139     if (png_get_gAMA(d->png_ptr, d->info_ptr, &image_gamma)) {
00140       png_set_gamma(d->png_ptr, screen_gamma, image_gamma);
00141     } else {
00142       png_set_gamma(d->png_ptr, screen_gamma, 0.45455);
00143     }
00144   }
00145
00146   /* Turn on interlace handling.  REQUIRED if you are not using
00147    * png_read_image().  To see how to handle interlacing passes,
00148    * see the png_read_row() method below: */
00149   d->number_passes = png_set_interlace_handling(d->png_ptr);
00150
00151   /* Optional call to gamma correct and add the background to the palette
00152    * and update info structure.  REQUIRED if you are expecting libpng to
00153    * update the palette for you (ie you selected such a transform above). */
00154   png_read_update_info(d->png_ptr, d->info_ptr);
00155
00156   return d;
00157 }
00158 
00159 /** Destructor. */
00160 PNGReader::~PNGReader()
00161 {
00162   fclose( __d->infile );
00163   /* clean up after the read, and free any memory allocated - REQUIRED */
00164   png_destroy_read_struct(&__d->png_ptr, &__d->info_ptr, png_infopp_NULL);
00165
00166   delete __d;
00167
00168   opened = false;
00169 }
00170
00171
00172 void
00173 PNGReader::set_buffer(unsigned char *yuv422planar_buffer)
00174 {
00175   buffer = yuv422planar_buffer;
00176 }
00177
00178
00179 colorspace_t
00180 PNGReader::colorspace()
00181 {
00182   return YUV422_PLANAR;
00183 }
00184
00185
00186 unsigned int
00187 PNGReader::pixel_width()
00188 {
00189   if ( opened ) {
00190     return png_get_image_width(__d->png_ptr, __d->info_ptr);
00191   } else {
00192     return 0;
00193   }
00194 }
00195
00196
00197 unsigned int
00198 PNGReader::pixel_height()
00199 {
00200   if ( opened ) {
00201     return png_get_image_height(__d->png_ptr, __d->info_ptr);
00202   } else {
00203     return 0;
00204   }
00205 }
00206
00207
00208 void
00209 PNGReader::read()
00210 {
00211   if ( buffer == NULL ) {
00212     throw Exception("PNGReader::read: buffer == NULL");
00213   }
00214   if ( __d->read ) {
00215     throw Exception("Can read PNG file only once.");
00216   }
00217   __d->read = true;
00218
00219   png_bytep row_pointer;
00220   row_pointer = (png_bytep)png_malloc(__d->png_ptr, png_get_rowbytes(__d->png_ptr, __d->info_ptr));
00221
00222   unsigned int lheight = pixel_height();
00223   unsigned int lwidth  = pixel_width();
00224
00225   for (int pass = 0; pass < __d->number_passes; ++pass) {
00226     for (unsigned y = 0; y < lheight; ++y) {
00227       png_read_rows(__d->png_ptr, &row_pointer, png_bytepp_NULL, 1);
00228       convert_line_rgb_to_yuv422planar( row_pointer, buffer, lwidth, lheight, 0, y );
00229     }
00230   }
00231
00232   /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
00233   png_read_end(__d->png_ptr, __d->info_ptr);
00234   png_free(__d->png_ptr, row_pointer);
00235
00236 }