Visualization Library

A lightweight C++ OpenGL middleware for 2D/3D graphics
[Home] [Tutorials] [All Classes] [Grouped Classes]

X:/dropbox/visualizationlibrary/src/vlCore/plugins/ioPNG.cpp

Go to the documentation of this file.
00001 /**************************************************************************************/
00002 /*                                                                                    */
00003 /*  Visualization Library                                                             */
00004 /*  http://www.visualizationlibrary.org                                               */
00005 /*                                                                                    */
00006 /*  Copyright (c) 2005-2010, Michele Bosi                                             */
00007 /*  All rights reserved.                                                              */
00008 /*                                                                                    */
00009 /*  Redistribution and use in source and binary forms, with or without modification,  */
00010 /*  are permitted provided that the following conditions are met:                     */
00011 /*                                                                                    */
00012 /*  - Redistributions of source code must retain the above copyright notice, this     */
00013 /*  list of conditions and the following disclaimer.                                  */
00014 /*                                                                                    */
00015 /*  - Redistributions in binary form must reproduce the above copyright notice, this  */
00016 /*  list of conditions and the following disclaimer in the documentation and/or       */
00017 /*  other materials provided with the distribution.                                   */
00018 /*                                                                                    */
00019 /*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND   */
00020 /*  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED     */
00021 /*  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE            */
00022 /*  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR  */
00023 /*  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES    */
00024 /*  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;      */
00025 /*  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON    */
00026 /*  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT           */
00027 /*  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS     */
00028 /*  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                      */
00029 /*                                                                                    */
00030 /**************************************************************************************/
00031 
00032 #include "ioPNG.hpp"
00033 #include <vlCore/LoadWriterManager.hpp>
00034 #include <vlCore/VisualizationLibrary.hpp>
00035 #include <vlCore/FileSystem.hpp>
00036 #include <vlCore/VirtualFile.hpp>
00037 #include <vlCore/Image.hpp>
00038 #include <png.h>
00039 
00040 using namespace vl;
00041 
00042 namespace
00043 {
00044   void png_read_vfile(png_structp png_ptr, png_bytep data, png_size_t byte_count)
00045   {
00046     VirtualFile* vfile = (VirtualFile*)png_get_io_ptr(png_ptr);
00047     vfile->read(data, byte_count);
00048   }
00049   void png_write_vfile(png_structp png_ptr, png_bytep data, png_size_t byte_count)
00050   {
00051     VirtualFile* vfile = (VirtualFile*)png_get_io_ptr(png_ptr);
00052     vfile->write(data,byte_count);
00053   }
00054   void png_flush_vfile(png_structp /*png_ptr*/)
00055   {
00056     // do nothing
00057   }
00058   void vl_error_fn(png_structp /*png_ptr*/, png_const_charp error_msg)
00059   {
00060     Log::error( Say("libPNG: %s\n") << error_msg);
00061   }
00062   void vl_warning_fn(png_structp /*png_ptr*/, png_const_charp warning_msg)
00063   {
00064     Log::warning( Say("libPNG: %s\n") << warning_msg);
00065   }
00066 }
00067 
00068 //-----------------------------------------------------------------------------
00069 ref<Image> vl::loadPNG(const String& path)
00070 {
00071   ref<VirtualFile> file = defFileSystem()->locateFile(path);
00072   if ( !file )
00073   {
00074     Log::error( Say("File '%s' not found.\n") << path );
00075     return NULL;
00076   }
00077   else
00078     return loadPNG(file.get());
00079 }
00080 //-----------------------------------------------------------------------------
00081 ref<Image> vl::loadPNG(VirtualFile* file)
00082 {
00083   if ( !file->open(OM_ReadOnly) )
00084   {
00085     Log::error( Say("loadPNG: cannot load PNG file '%s'\n") << file->path() );
00086     return NULL;
00087   }
00088 
00089   png_structp png_ptr;
00090   png_infop info_ptr;
00091   png_infop endinfo;
00092   // unsigned int sig_read = 0;
00093   png_uint_32 width, height;
00094   int bit_depth, color_type;
00095 
00096   /* Create and initialize the png_struct with the desired error handler
00097   * functions.  If you want to use the default stderr and longjump method,
00098   * you can supply NULL for the last three parameters.  We also supply the
00099   * the compiler header file version, so that we know if the application
00100   * was compiled with a compatible version of the library.  REQUIRED
00101   */
00102   png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
00103   png_set_error_fn(png_ptr, png_get_error_ptr(png_ptr), vl_error_fn, vl_warning_fn);
00104 
00105   if (png_ptr == NULL)
00106   {
00107     file->close();
00108     return NULL;
00109   }
00110 
00111   /* Allocate/initialize the memory for image information.  REQUIRED. */
00112   info_ptr = png_create_info_struct(png_ptr);
00113   endinfo  = png_create_info_struct(png_ptr);
00114   VL_CHECK(info_ptr)
00115   VL_CHECK(endinfo)
00116 
00117   // fixme? "warning C4611: interaction between '_setjmp' and C++ object destruction is non-portable"
00118 
00119 #if 0
00120   if (setjmp(png_jmpbuf(png_ptr)))
00121   {
00122     /* Free all of the memory associated with the png_ptr and info_ptr */
00123     png_destroy_read_struct(&png_ptr, &info_ptr, &endinfo);
00124     file->close();
00125     /* If we get here, we had a problem reading the file */
00126     return NULL;
00127   }
00128 #endif
00129 
00130   unsigned char header[8];
00131   int count = (int)file->read(header,8);
00132   if (count == 8 && png_check_sig(header, 8))
00133   {
00134     png_set_read_fn(png_ptr,file,png_read_vfile);
00135     png_set_sig_bytes(png_ptr, 8);
00136   }
00137   else
00138   {
00139     png_destroy_read_struct(&png_ptr, &info_ptr, &endinfo);
00140     file->close();
00141     return NULL;
00142   }
00143 
00144   /* The call to png_read_info() gives us all of the information from the
00145   * PNG file before the first IDAT (image data chunk).  REQUIRED
00146   */
00147   png_read_info(png_ptr, info_ptr);
00148 
00149   png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL/*&interlace_type*/, NULL/*int_p_NULL*/, NULL/*int_p_NULL*/);
00150 
00151   ref<Image> img = new Image;
00152   img->setObjectName(file->path().toStdString().c_str());
00153 
00154   if (bit_depth == 16)
00155   {
00156     switch(color_type)
00157     {
00158       case PNG_COLOR_TYPE_GRAY:       img->allocate2D(width, height, 1, IF_LUMINANCE, IT_UNSIGNED_SHORT); break;
00159       case PNG_COLOR_TYPE_GRAY_ALPHA: img->allocate2D(width, height, 1, IF_LUMINANCE_ALPHA, IT_UNSIGNED_SHORT); break;
00160       case PNG_COLOR_TYPE_PALETTE:    img->allocate2D(width, height, 1, IF_RGB, IT_UNSIGNED_SHORT); break;
00161       case PNG_COLOR_TYPE_RGB:        img->allocate2D(width, height, 1, IF_RGB, IT_UNSIGNED_SHORT); break;
00162       case PNG_COLOR_TYPE_RGB_ALPHA:  img->allocate2D(width, height, 1, IF_RGBA, IT_UNSIGNED_SHORT); break;
00163       default:
00164         VL_TRAP()
00165         break;
00166     }
00167   }
00168   else
00169   {
00170     switch(color_type)
00171     {
00172       case PNG_COLOR_TYPE_GRAY:       img->allocate2D(width, height, 1, IF_LUMINANCE, IT_UNSIGNED_BYTE); break;
00173       case PNG_COLOR_TYPE_GRAY_ALPHA: img->allocate2D(width, height, 1, IF_LUMINANCE_ALPHA, IT_UNSIGNED_BYTE); break;
00174       case PNG_COLOR_TYPE_PALETTE:    img->allocate2D(width, height, 1, IF_RGBA, IT_UNSIGNED_BYTE); break;
00175       case PNG_COLOR_TYPE_RGB:        img->allocate2D(width, height, 1, IF_RGB, IT_UNSIGNED_BYTE); break;
00176       case PNG_COLOR_TYPE_RGB_ALPHA:  img->allocate2D(width, height, 1, IF_RGBA, IT_UNSIGNED_BYTE); break;
00177       default:
00178         VL_TRAP()
00179         break;
00180     }
00181   }
00182 
00183   /* Set up the data transformations you want.  Note that these are all
00184    * optional.  Only call them if you want/need them.  Many of the
00185    * transformations only work on specific types of images, and many
00186    * are mutually exclusive.
00187    */
00188 
00189    /* tell libpng to strip 16 bit/color files down to 8 bits/color */
00190    // png_set_strip_16(png_ptr);
00191 
00192    /* Strip alpha bytes from the input data without combining with the
00193     * background (not recommended).
00194     */
00196 
00197    /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single
00198     * byte into separate bytes (useful for paletted and grayscale images).
00199     */
00200    png_set_packing(png_ptr);
00201 
00202    /* Change the order of packed pixels to least significant bit first
00203     * (not useful if you are using png_set_packing). */
00204    // png_set_packswap(png_ptr);
00205 
00206    /* Expand paletted colors into true RGB triplets */
00207    if (color_type == PNG_COLOR_TYPE_PALETTE)
00208       png_set_palette_to_rgb(png_ptr);
00209 
00210   /* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */
00211   if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
00212     png_set_expand_gray_1_2_4_to_8(png_ptr);
00213 
00214   #if 1
00215     /* Expand paletted or RGB images with transparency to full alpha channels
00216     * so the data will be available as RGBA quartets.
00217     */
00218     if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
00219       png_set_tRNS_to_alpha(png_ptr);
00220   #endif
00221 
00222   #if 0
00223    /* Set the background color to draw transparent and alpha images over.
00224     * It is possible to set the red, green, and blue components directly
00225     * for paletted images instead of supplying a palette index.  Note that
00226     * even if the PNG file supplies a background, you are not required to
00227     * use it - you should use the (solid) application background if it has one.
00228     */
00229    png_color_16 my_background, *image_background;
00230    if (png_get_bKGD(png_ptr, info_ptr, &image_background))
00231       png_set_background(png_ptr, image_background, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
00232    else
00233       png_set_background(png_ptr, &my_background, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
00234   #endif
00235 
00236    // char* gamma_str = NULL;
00237    double screen_gamma = 2.2; /* A good guess for a PC monitors in a dimly lit room */
00238    // double screen_gamma = 1.7 or 1.0;  /* A good guess for Mac systems */
00239 
00240    /*if ((gamma_str = getenv("SCREEN_GAMMA")) != NULL)
00241       screen_gamma = atof(gamma_str);*/
00242 
00243    /* Tell libpng to handle the gamma conversion for you.  The final call
00244     * is a good guess for PC generated images, but it should be configurable
00245     * by the user at run time by the user.  It is strongly suggested that
00246     * your application support gamma correction.
00247     */
00248 
00249     double image_gamma;
00250     if (png_get_gAMA(png_ptr, info_ptr, &image_gamma))
00251        png_set_gamma(png_ptr, screen_gamma, image_gamma);
00252     else
00253        png_set_gamma(png_ptr, screen_gamma, 1.0/screen_gamma/*0.45455*/);
00254 
00255 #if 0
00256    /* Dither RGB files down to 8 bit palette or reduce palettes
00257     * to the number of colors available on your screen.
00258     */
00259    if (color_type & PNG_COLOR_MASK_COLOR)
00260    {
00261       int num_palette;
00262       png_colorp palette;
00263 
00264       * This reduces the image to the application supplied palette */
00265       if (/* we have our own palette */)
00266       {
00267          /* An array of colors to which the image should be dithered */
00268          png_color std_color_cube[MAX_SCREEN_COLORS];
00269 
00270          png_set_dither(png_ptr, std_color_cube, MAX_SCREEN_COLORS,
00271             MAX_SCREEN_COLORS, png_uint_16p_NULL, 0);
00272       }
00273       * This reduces the image to the palette supplied in the file */
00274       else 
00275       if (png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette))
00276       {
00277          png_uint_16p histogram = NULL;
00278 
00279          png_get_hIST(png_ptr, info_ptr, &histogram);
00280 
00281          png_set_dither(png_ptr, palette, num_palette,
00282                         max_screen_colors, histogram, 0);
00283       }
00284    }
00285 #endif
00286 
00287 #if 0
00288    /* invert monochrome files to have 0 as white and 1 as black */
00289    png_set_invert_mono(png_ptr);
00290 #endif
00291 
00292 #if 0
00293    /* If you want to shift the pixel values from the range [0,255] or
00294     * [0,65535] to the original [0,7] or [0,31], or whatever range the
00295     * colors were originally in:
00296     */
00297    if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sBIT))
00298    {
00299       png_color_8p sig_bit;
00300 
00301       png_get_sBIT(png_ptr, info_ptr, &sig_bit);
00302       png_set_shift(png_ptr, sig_bit);
00303    }
00304 #endif
00305 
00306 #if 0
00307    /* flip the RGB pixels to BGR (or RGBA to BGRA) */
00308    if (color_type & PNG_COLOR_MASK_COLOR)
00309       png_set_bgr(png_ptr);
00310 #endif
00311 
00312 #if 0
00313    /* swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) */
00314    png_set_swap_alpha(png_ptr);
00315 #endif
00316 
00317 #if 1
00318    /* swap bytes of 16 bit files to least significant byte first */
00319     unsigned short bet = 0x00FF;
00320     bool little_endian_cpu = ((unsigned char*)&bet)[0] == 0xFF;
00321     if (little_endian_cpu && bit_depth > 8)
00322       png_set_swap(png_ptr);
00323 #endif
00324 
00325 #if 0
00326    /* Add filler (or alpha) byte (before/after each RGB triplet) */
00327    png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
00328 #endif
00329 
00331    // * png_read_image().  To see how to handle interlacing passes,
00332    // * see the png_read_row() method below:
00333    // */
00334    //int number_passes = png_set_interlace_handling(png_ptr);
00335 
00337    // * and update info structure.  REQUIRED if you are expecting libpng to
00338    // * update the palette for you (ie you selected such a transform above).
00339    // */
00340    //png_read_update_info(png_ptr, info_ptr);
00341 
00343 
00346    //std::vector<png_bytep> row_pointers;
00347    //row_pointers.resize(height);
00348 
00349    //for (unsigned row = 0; row < height; row++)
00350    //{
00351    //   row_pointers[row] = (png_bytep)png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr));
00352    //}
00353 
00354    //for (int pass = 0; pass < number_passes; pass++)
00355    //{
00356    //   for (unsigned y = 0; y < height; y++)
00357    //   {
00358    //      png_read_rows(png_ptr, &row_pointers[y], png_bytepp_NULL, 1);
00359    //   }
00360    //}
00361    /* At this point you have read the entire image */
00362   
00363    // initialize row pointers
00364    std::vector<png_bytep> row_p;
00365    row_p.resize(height);
00366    for(unsigned i=0; i<height; ++i)
00367      row_p[height - 1 - i] = (png_bytep)img->pixels()+img->pitch()*i;
00368 
00369    png_read_image(png_ptr, &row_p[0]);
00370    png_read_end(png_ptr, endinfo);
00371 
00372    /* clean up after the read, and free any memory allocated - REQUIRED */
00373    png_destroy_read_struct(&png_ptr, &info_ptr, &endinfo);
00374 
00375    file->close();
00376    return img;
00377 }
00378 //-----------------------------------------------------------------------------
00379 bool vl::isPNG(VirtualFile* file)
00380 {
00381   if ( !file->open(OM_ReadOnly) )
00382   {
00383     // Log::error( Say("loadPNG: cannot load PNG file '%s'\n") << file->path() );
00384     return false;
00385   }
00386 
00387   unsigned char header[8];
00388   int count = (int)file->read(header,8);
00389   file->close();
00390   if (count == 8 && png_check_sig(header, 8))
00391     return true;
00392   else
00393     return false;
00394 }
00395 //-----------------------------------------------------------------------------
00396 bool vl::savePNG(const Image* src, const String& path, int compression)
00397 {
00398   ref<DiskFile> file = new DiskFile(path);
00399   return savePNG(src, file.get(), compression);
00400 }
00401 //-----------------------------------------------------------------------------
00402 bool vl::savePNG(const Image* src, VirtualFile* fout, int compression)
00403 {
00404   /*if ( src->dimension() != ID_2D && src->dimension() != ID_1D && src->dimension() != ID_3D )
00405   {
00406     Log::error( Say("savePNG('%s'): can save only 1D, 2D and 3D images.\n") << fout->path() );
00407     return false;
00408   }*/
00409 
00410   int w = src->width();
00411   int h = src->height();
00412   int d = src->depth();
00413   if (h == 0) h=1;
00414   if (d == 0) d=1;
00415   if (src->isCubemap()) d=6;
00416   h = h*d;
00417 
00418   ref<Image> cimg;
00419   // convert to IT_UNSIGNED_SHORT if necessary
00420   if (src->type() == IT_FLOAT || src->type() == IT_SHORT)
00421   {
00422     cimg = src->convertType(IT_UNSIGNED_SHORT);
00423     src = cimg.get();
00424     if (!cimg)
00425     {
00426       Log::error( Say("savePNG('%s'): could not convert image to IT_UNSIGNED_SHORT.\n") << fout->path() );
00427       return false;
00428     }
00429   }
00430 
00431   bool format_ok = src->format() == IF_RGB || src->format() == IF_RGBA || src->format() == IF_LUMINANCE || src->format() == IF_ALPHA || src->format() == IF_LUMINANCE_ALPHA;
00432   if (!format_ok)
00433   {
00434     cimg = src->convertFormat(IF_RGBA);
00435     src = cimg.get();
00436     if (!cimg)
00437     {
00438       Log::error( Say("savePNG('%s'): could not convert image to IF_RGBA.\n") << fout->path() );
00439       return false;
00440     }
00441   }
00442 
00443   VL_CHECK(src->type() == IT_UNSIGNED_BYTE || src->type() == IT_UNSIGNED_SHORT)
00444   VL_CHECK(src->format() == IF_RGB || src->format() == IF_RGBA || src->format() == IF_LUMINANCE || src->format() == IF_ALPHA || src->format() == IF_LUMINANCE_ALPHA)
00445 
00446   if(!fout->open(OM_WriteOnly))
00447   {
00448     Log::error( Say("PNG: could not write to '%s'.\n") << fout->path() );
00449     return false;
00450   }
00451 
00452   png_structp png  = NULL;
00453   png_infop   info = NULL;
00454 
00455   png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
00456   if(!png) 
00457     return false;
00458 
00459   info = png_create_info_struct(png);
00460   if(!info) 
00461     return false;
00462 
00463   png_set_write_fn(png,fout,png_write_vfile,png_flush_vfile);
00464 
00465   png_set_compression_level(png, compression);
00466 
00467   std::vector< png_bytep > rows;
00468   rows.resize(h);
00469   for(int i=0, y = h-1; i<h; ++i,--y)
00470     rows[i] = (png_bytep)(src->pixels()+src->pitch()*y);
00471 
00472   int color;
00473   switch(src->format()) 
00474   {
00475       case IF_RGB:             color = PNG_COLOR_TYPE_RGB; break;
00476       case IF_RGBA:            color = PNG_COLOR_TYPE_RGB_ALPHA; break;
00477       case IF_ALPHA:           
00478       case IF_LUMINANCE:       color = PNG_COLOR_TYPE_GRAY; break;
00479       case IF_LUMINANCE_ALPHA: color = PNG_COLOR_TYPE_GRAY_ALPHA; break;
00480       default: 
00481         return false;
00482   }
00483 
00484   int bit_depth = src->type() == IT_UNSIGNED_SHORT ? 16 : 8;
00485 
00486   png_set_IHDR( png, info, w, h, bit_depth, color, 
00487                 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
00488 
00489   unsigned short bet = 0x00FF;
00490   bool little_endian_cpu = ((unsigned char*)&bet)[0] == 0xFF;
00491   if (little_endian_cpu && bit_depth == 16)
00492   {
00493     png_set_rows(png, info, &rows[0]);
00494     png_write_png(png, info, PNG_TRANSFORM_SWAP_ENDIAN, NULL);
00495   }
00496   else
00497   {
00498     png_write_info(png, info);
00499     png_write_image(png, &rows[0]);
00500     png_write_end(png, NULL);
00501   }
00502 
00503   png_destroy_write_struct(&png,&info);
00504 
00505   fout->close();
00506   return true;
00507 }
00508 //-----------------------------------------------------------------------------

Visualization Library 2011.09.1160 Reference Documentation
Copyright 2005-2011 Michele Bosi. All rights reserved.
Updated on Thu May 2 2013 13:40:30.
Permission is granted to use this page to write and publish articles regarding Visualization Library.