# Visualization Library

A lightweight C++ OpenGL middleware for 2D/3D graphics

[Home] [Tutorials] [All Classes] [Grouped Classes]

- Main Page
- Related Pages
- Namespaces
- Classes
- Files

- File List
- File Members

# D:/VL/BuildSystem/VL_DISTRO/Visualization_Library_SDK/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 v2011.09.1162 Reference Documentation
Copyright 2005-2011 Michele Bosi. All rights reserved.
Updated on Thu Jan 26 2012 00:43:30.
Permission is granted to use this page to write and publish articles regarding Visualization Library.