Visualization Library v1.0.3A lightweight C++ OpenGL middleware for 2D/3D graphics |
[Download] [Tutorials] [All Classes] [Grouped Classes] |
00001 /**************************************************************************************/ 00002 /* */ 00003 /* Visualization Library */ 00004 /* http://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 //-----------------------------------------------------------------------------